/*******************************************************************************
* NXP Semiconductors
* (c) Copyright 2016 NXP Semiconductors
* ALL RIGHTS RESERVED.
********************************************************************************
Services performed by NXP in this matter are performed AS IS and without any 
warranty. CUSTOMER retains the final decision relative to the total design
and functionality of the end product. NXP neither guarantees nor will be held
liable by CUSTOMER for the success of this project.
NXP DISCLAIMS ALL WARRANTIES, EXPRESSED, IMPLIED OR STATUTORY INCLUDING,
BUT NOT LIMITED TO, IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
A PARTICULAR PURPOSE ON ANY HARDWARE, SOFTWARE ORE ADVISE SUPPLIED 
TO THE PROJECT BY NXP, AND OR NAY PRODUCT RESULTING FROM NXP SERVICES. 
IN NO EVENT SHALL NXP BE LIABLE FOR INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THIS AGREEMENT. 
CUSTOMER agrees to hold NXP harmless against any and all claims demands 
or actions by anyone on account of any damage, or injury, whether commercial,
contractual, or tortuous, rising directly or indirectly as a result 
of the advise or assistance supplied CUSTOMER in connection with product, 
services or goods supplied under this Agreement.
********************************************************************************
* File              serial_communication.c
* Owner             Lukas Zadrapa
* Version           1.0
* Date              Jun-20-2016
* Classification    General Business Information
* Brief             Serial communication
********************************************************************************
* Detailed Description:
********************************************************************************
Revision History:
Ver  Date         Author            Description of Changes
1.0  Jun-20-2016  Lukas Zadrapa     Initial version
*******************************************************************************/

/*******************************************************************************
* Includes                                                                     
*******************************************************************************/
#include "MPC5748G_310.h"
#include "serial_communication.h"

/*******************************************************************************
* External objects
*******************************************************************************/

/*******************************************************************************
* Constants and macros
*******************************************************************************/
#define XOn 0x11
#define XOff 0x13

#define RxBufSize 96
#define TxBufSize 64

#define XOnCount RxBufSize - 8
#define XOffCount (RxBufSize-16)+2

/*******************************************************************************
* Global variables
*******************************************************************************/
volatile uint8_t RxBuff[RxBufSize];  /* receive queue */
volatile uint8_t TxBuff[TxBufSize];  /* transmit queue */
volatile uint8_t RxIn;               /* next available location in the Rx queue */
volatile uint8_t RxOut;              /* next character to be removed from the Rx queue */
volatile uint8_t TxIn;               /* next available location in the Tx queue */
volatile uint8_t TxOut;              /* next character to be sent from the Tx queue */
volatile uint8_t RxBAvail;  /* number of bytes left in the Rx queue */
volatile uint8_t TxBAvail;  /* number of bytes left in the Tx queue */

volatile uint8_t XOffSent;
/* flag indicating that an XOff character was sent (== 1, XOff was sent) */
  
volatile uint8_t XOffRcvd;
/* flag indicating that an XOff character was received (== 1, XOff was received) */
  
volatile uint8_t SendXOff;
/* flag indicating that the TxISR should send an XOff the next time it's called */
  
volatile uint8_t SendXOn;

volatile uint8_t TxInProgress;  /* char has been sent, waiting for Tx complete */

/*******************************************************************************
* Local types
*******************************************************************************/

/*******************************************************************************
* Local function prototypes
*******************************************************************************/

/*******************************************************************************
* Local variables
*******************************************************************************/

/*******************************************************************************
* Local functions
*******************************************************************************/

/*******************************************************************************
* Global functions
*******************************************************************************/

/*******************************************************************************
Function Name : LINFlexD_2_Init
Engineer      : Lukas Zadrapa
Date          : Jun-20-2016
Parameters    : NONE
Modifies      : NONE
Returns       : NONE
Notes         : Initializes MPC5748G's LINFlexD_2 module for UART mode.
                Expecting system clock is 160MHz, peripheral 
Issues        : NONE
*******************************************************************************/
void LINFlexD_2_Init (void)
{
    RxIn = RxOut = TxIn = TxOut = 0;  /* set the Rx & Tx queue indexes to zero */
    RxBAvail = RxBufSize;     /* the receive buffer is empty */
    TxBAvail = TxBufSize;     /* the transmit buffer is empty */
    XOffSent = 0;             /* XOff character has not been sent */
    XOffRcvd = 0;             /* XOff character has not been received */
    SendXOff = 0;             /* Don't send an XOff */

    /* enter INIT mode */
    LINFlexD_2.LINCR1.R = 0x0081; /* SLEEP=0, INIT=1 */

    /* wait for the INIT mode */
    while (0x1000 != (LINFlexD_2.LINSR.R & 0xF000)) {}

    /* configure pads */
    SIUL2.MSCR[40].R = 0x02840001;     /* Configure pad PC8 for LIN2TX */

    SIUL2.MSCR[41].R = 0x008C0000;     /* Configure pad PC9 for LIN2RX */
    SIUL2.IMCR[202].R = 0x2;

    /* configure for UART mode */
    LINFlexD_2.UARTCR.R = 0x0001; /* set the UART bit first to be able to write the other bits */

    LINFlexD_2.UARTCR.R = 0x0033; /* 8bit data, no parity, Tx and Rx enabled, UART mode */
    /* Transmit buffer size = 1 (TDFL = 0 */
    /* Receive buffer size = 1 (RDFL = 0) */

    /* configure baudrate 115200 */
    /* assuming 80 MHz peripheral clock (F80)*/
    /* LDIV = fperiph / (16 * desired baudrate)
       LINIBRR = integer part of LFDIV
       LINFBRR = 16 * fractional part of LFDIV (after decimal point)

       LFDIV = 80MHz/(16*115200) = 43,403
       LINIBRR = 43
       LINFBRR = 16*0.403 = 6
       */
    LINFlexD_2.LINIBRR.R = 43;
    LINFlexD_2.LINFBRR.R = 6;


    LINFlexD_2.LINIER.B.DRIE = 1;	/* Data reception interrupt enable */

    /* enter NORMAL mode */
    LINFlexD_2.LINCR1.R = 0x0080; /* INIT=0 */

    INTC.PSR[382].R = 0x8001;	/* interrupt routed to core 0, priority 1 */
    INTC.PSR[383].R = 0x8001;	/* interrupt routed to core 0, priority 1 */
}//LINFlexD_2_Init 

/*******************************************************************************
Function Name : PutChar
Engineer      : Lukas Zadrapa
Date          : Jun-20-2016
Parameters    : NONE
Modifies      : NONE
Returns       : NONE
Notes         : Transmit one character to serial interface and wait for done
Issues        : NONE
*******************************************************************************/
void PutChar(int8_t c)
{
    while (TxBAvail == 0){};    /* if there's no room in the xmit queue wait here */

    asm("wrteei 0");		/* disable interrupts */
    TxBuff[TxIn++] = (int8_t)c; /* put the char in the buffer, inc buffer index */
    if (TxIn == TxBufSize)     	/* buffer index go past the end of the buffer? */
	TxIn = 0;               /* yes. wrap around to the start */
    TxBAvail--;                	/* one less character available in the buffer */
    if (XOffRcvd == 0)         	/* if an XOff has not been received... */
    {
	if(TxInProgress == 0)
	{
	    /* write character to transmit buffer */
	    LINFlexD_2.BDRL.B.DATA0 = TxBuff[TxOut++];
	    /* Data Transmitted Interrupt Enable */
	    LINFlexD_2.LINIER.B.DTIE = 1;
	    TxInProgress = 1;

	    if (TxOut == TxBufSize)	/* reached the physical end of the buffer? */
		TxOut = 0;              /* yes. wrap around to the start */
	    TxBAvail++;
	}	  
    }

    asm("wrteei 1");	/* enable interrupts */
}//PutChar

/*******************************************************************************
Function Name : GetChar
Engineer      : Lukas Zadrapa
Date          : Jun-20-2016
Parameters    : NONE
Modifies      : NONE
Returns       : Returns received character
Notes         : Wait until character is received and return the character
Issues        : NONE
*******************************************************************************/
int8_t GetChar(void)
{
    uint8_t c;                 	/* holds the character we'll return */

    /* if there's no characters in the Rx buffer wait here */
    while (RxBAvail == RxBufSize){};

    asm("wrteei 0");						/* disable interrupts */

    /* get a character from the Rx buffer & advance the Rx index */
    c = RxBuff[RxOut++];
    if (RxOut == RxBufSize)    	/* index go past the physical end of the buffer? */
	RxOut = 0;                /* yes wrap around to the start */
    RxBAvail++;                	/* 1 more byte available in the receive buffer */

    /* if an XOff was previously sent & the Rx buffer
       has more than XOnCount bytes available */
    if ((XOffSent != 0) & (RxBAvail >= XOnCount))
    {
	if(TxInProgress == 0)
	{
	    /* write character to transmit buffer */
	    LINFlexD_2.BDRL.B.DATA0 = XOn;
	    /* Data Transmitted Interrupt Enable */
	    LINFlexD_2.LINIER.B.DTIE = 1;
	    XOffSent = 0;
	    TxInProgress = 1;
	}
	else
	    SendXOn = 1;	  
    }
    asm("wrteei 1");						/* enable interrupts */

    return(c);     /* return the character retrieved from receive buffer */	
}//GetChar

/*******************************************************************************
Function Name : OutStr
Engineer      : Lukas Zadrapa
Date          : Jun-20-2016
Parameters    : NONE
Modifies      : NONE
Returns       : NONE
Notes         : Transmit string. String must be finished by null character.
Issues        : NONE
*******************************************************************************/
void OutStr(char *Str)
{
    /* output 1 character at a time untill we reach the end of the string */
    while (*Str != 0)
	PutChar(*Str++);
}//OutStr

/*******************************************************************************
Function Name : LINFlexD_2_RX_ISR
Engineer      : Lukas Zadrapa
Date          : Jun-20-2016
Parameters    : NONE
Modifies      : NONE
Returns       : NONE
Notes         : Data reception complete ISR handler
Issues        : NONE
*******************************************************************************/
#pragma ghs section text=".ram_code"
void LINFlexD_2_RX_ISR(void)
{
    uint8_t c;

    /* Wait for Release Message Buffer */
    while (1 != LINFlexD_2.UARTSR.B.RMB) {}    
    /* Read data from receive buffer */
    c = LINFlexD_2.BDRM.B.DATA4;;
    /* Clear the RMB and DRFRFE flags by writing 1 to them */
    LINFlexD_2.UARTSR.R = 0x0204;

    if (c == XOff)                	/* host want us to stop sending data? */
    {    
	XOffRcvd = 1; /* let putchar know that it may continue to place characters */
	/* in the buffer but not enable Xmit interrupts */
	return;
    }
    else if (c == XOn)            	/* host want us to start sending data? */
    {
	if (TxBAvail != TxBufSize)  	/* anything left in the Tx buffer? */

	    if(TxInProgress == 0)
	    {
		/* write character to transmit buffer */
		LINFlexD_2.BDRL.B.DATA0 = TxBuff[TxOut++];
		/* Data Transmitted Interrupt Enable */
		LINFlexD_2.LINIER.B.DTIE = 1;
		TxInProgress = 1;

		if (TxOut == TxBufSize) /* reached the physical end of the buffer? */
		    TxOut = 0;          /* yes. wrap around to the start */
		TxBAvail++;
	    }

	XOffRcvd = 0;
	return;
    }

    /* if the number of bytes available in the Rx buff 
       is < XOffCount & an XOff hasn't been sent */
    if ((RxBAvail <= XOffCount) && (XOffSent == 0))
    { 
	if(TxInProgress == 0)
	{
	    /* write character to transmit buffer */
	    LINFlexD_2.BDRL.B.DATA0 = XOff;
	    /* Data Transmitted Interrupt Enable */
	    LINFlexD_2.LINIER.B.DTIE = 1;
	    TxInProgress = 1;
	    XOffSent = 1; /* set the flag showing that we've queued up to send an XOff */  
	}
	else
	    SendXOff = 1; /* set flag to send an XOff, stoping the host from sending data */        
    }

    if (RxBAvail != 0)            	/* if there are bytes available in the Rx buffer */
    {
	RxBAvail--;                 	/* reduce the count by 1 */
	RxBuff[RxIn++] = c;         	/* place the received byte in the buffer */
	if (RxIn == RxBufSize)      	/* reached the physical end of the buffer? */
	    RxIn = 0;                   /* yes. wrap around to the start */
    }
}//LINFlexD_2_RX_ISR
#pragma ghs section text=default

/*******************************************************************************
Function Name : LINFlexD_2_TX_ISR
Engineer      : Lukas Zadrapa
Date          : Jun-20-2016
Parameters    : NONE
Modifies      : NONE
Returns       : NONE
Notes         : Data transmission ISR handler
Issues        : NONE
*******************************************************************************/
#pragma ghs section text=".ram_code"
void LINFlexD_2_TX_ISR(void)
{
    /* Clear the DTFTFF flag and not the other flags */
    LINFlexD_2.UARTSR.R = 0x0002;

    if (SendXOn != 0)             	/* request to send an XOn to the host? */
    {
	SendXOn = 0;
	XOffSent = 0;               	/* reset the XOff flag */

	/* write character to transmit buffer */
	LINFlexD_2.BDRL.B.DATA0 = XOn;
	/* Data Transmitted Interrupt Enable */   
	TxInProgress = 1;                
    }

    else if (SendXOff != 0)       	/* request to send an XOff to the host? */
    {
	SendXOff = 0;               	/* yes, clear the request */

	/* write character to transmit buffer */
	LINFlexD_2.BDRL.B.DATA0 = XOff;
	/* Data Transmitted Interrupt Enable */   
	TxInProgress = 1;        
    }

    /* if anything left in the Tx buffer and XOff was not received, send data */
    else if((TxBAvail != TxBufSize) && (XOffRcvd == 0) )	
    {
	/* write character to transmit buffer */
	LINFlexD_2.BDRL.B.DATA0 = TxBuff[TxOut++];
	/* Data Transmitted Interrupt Enable */
	LINFlexD_2.LINIER.B.DTIE = 1;
	TxInProgress = 1;

	if (TxOut == TxBufSize)     	/* reached the physical end of the buffer? */
	    TxOut = 0;                	/* yes. wrap around to the start */
	TxBAvail++;
    }
    else
    {
	/* Data Transmitted Interrupt disable */
	LINFlexD_2.LINIER.B.DTIE = 0;
	TxInProgress = 0;
    }
}//LINFlexD_2_TX_ISR
#pragma ghs section text=default
