/***************************************************************************//**
 *   @file   ECAN1Drv.c
 *   @brief  Contient les fonctions de gestion du bus CAN
 *   @author Kévin MARCHAND(Kevin.marchand@intelligence-electronique.com)
********************************************************************************
 * Copyright 2015(c) PLCD, Inc.
 *
 * Fonctions de gestion du bus CAN
 *
********************************************************************************
 *   SVN Revision: 1
*******************************************************************************/
#if defined(__dsPIC33E__)
#include "p33exxxx.h"
#elif defined(__PIC24E__)
#include "p24exxxx.h"
#endif

#include "ECAN1Drv.h"
#include "ECAN1Node.h"

// Create the message arrays.
__eds__ int ecanTxMsgBuffer[BUFFER_TX_SIZE] __attribute__((space(dma),eds,aligned(256)));
__eds__ int ecanRxMsgBuffer[NUM_OF_RX_BUFFERS][BUFFER_RX_SIZE] __attribute__((space(dma),eds,aligned(256)));

/* The received flag is checked in the main loop
 * to compute CRC on the received message
 * and flags errors. The transmitNext flag
 * transmits the next packet. The sentPacket
 * variable tracks the number of packets
 * sent.*/
volatile int receivedECANmsg = 0;
volatile int transmitNextECANmsg = 0;
int sentPacketECANMSG = 0;

/* In Register Indirect mode, the ECAN/DMA
 * cannot differentiate between buffers. The
 * application must keep track of where the
 * received message was stored */
volatile int rxECANBufferIndex = -1;
int rxECANIndex;
int txECANBufferIndex = 0;


/***************************************************************************//**
 * @brief This function returns the value to be stored in CxFnSID register.
 * @param SID - The Standard ID.
 * @param EID - The Extended ID. For Standard messages EID should be zero.
 * @param EXIDE - 0 for Standard messages. 1 otherwise
 * @return value to be stored in CxFnSID register.
*******************************************************************************/
unsigned int ECANFilterSID(int sid, unsigned long eid, int exide)
{ 
    unsigned int returnVal;

    returnVal = sid << 5;
    if(exide == 1)
        returnVal |= 0x8;
    else
        returnVal &= 0xFFFE;
    returnVal |= (int)(eid >> 16);
    return (returnVal);
}	
/***************************************************************************//**
 * @brief This function returns the value to be stored in CxFnEID register.
 * @param EID - The Extended ID.
 * @return value to be stored in CxFnEID register.
*******************************************************************************/
unsigned int ECANFilterEID( unsigned long eid)
{	 
    unsigned int returnVal;

    returnVal = (int)(eid & 0xFFFF);

    return(returnVal);
}	
/***************************************************************************//**
 * @brief Set up DMA Channel 2 to copy data from DMA RAM to ECAN module 1.
 * @details The DMA mode is Register Indirect with Post Increment.
 *          The length of each ECAN message is 7 words.
 *          Additionally the Continuous mode with ping pong disabled is used.
 *          For ease of error handling while using register indirect mode, only
 *          one TX buffer is used. 
 * @param numOfRxBuffers - Number of Buffers.
 * @return none.
*******************************************************************************/
void ECAN1DMAConfig(int numOfRxBuffers)
{
	DMA2CON = 0x2000;
	DMA2PAD = (int)(&C1TXD);
 	DMA2CNT = BUFFER_TX_SIZE - 1;
	DMA2REQ = 0x0046;
	//DMA0STA = txBufferOffset;
        DMA2STAL = __builtin_dmaoffset(&ecanTxMsgBuffer);
        DMA2STAH = __builtin_dmapage(&ecanTxMsgBuffer);

   	_DMA2IE	= 0;
   	_DMA2IF = 0;
   	DMA2CONbits.CHEN = 1;
	
	/* Set up DMA Channel 1 to copy data from
	 * ECAN module 1 to DMA RAM. The Receive
	 * memory is treated like a FIFO. The ECAN
	 * module cannot differentiate between buffers
	 * when the DMA is in register indirect mode.
	 * Note the size of each recevied message is 
	 * eight words. Continuous with pin pong disabled
	 * is used.*/

	DMA1CON = 0x0000;		
	DMA1PAD = (int)(&C1RXD);	
 	DMA1CNT = (numOfRxBuffers * BUFFER_RX_SIZE) - 1;
	DMA1REQ = 0x0022;	
	//DMA1STA = rxBufferOffset;
        DMA1STAL = __builtin_dmaoffset(&ecanRxMsgBuffer);
        DMA1STAH = __builtin_dmapage(&ecanRxMsgBuffer);
        _DMA1IE	= 0;
   	_DMA1IF = 0;
   	DMA1CONbits.CHEN = 1;		
}    	


/***************************************************************************//**
 * @brief ECAN_FCY and ECAN_BITRATE are defined in ECAN1Drv.h
 * @details The total time time quanta per bit is 8. Refer to ECAN FRM section
 *          for more details on setting the CAN bit rate.
 * @return none.
*******************************************************************************/
void ECAN1ClockConfig(void)
{
    C1CTRL1bits.CANCKS = 1;
    C1CFG1 = ((ECAN_FCY/16)/ECAN_BITRATE)-1;
    C1CFG2 = 0x0290;
} 

/***************************************************************************//**
 * @brief Only the C1 Event Interrupt is used in this example.
 * @details All the status flags are cleared before the module is enabled.
 * @return none.
*******************************************************************************/
void ECAN1InterruptConfig(void)
{
    C1INTF = 0;
    _C1IF = 0;
    _C1TXIF = 0;
    _C1RXIF = 0;
    _C1IE = 1;
    _C1TXIE = 0;
    _C1RXIE = 0;
    C1INTE = 0x00FF;
    C1RXFUL1 = 0;
    C1RXFUL2 = 0;
    C1RXOVF1 = 0;
    C1RXOVF2 = 0;
}

/***************************************************************************//**
 * @brief Configure only one TX buffer and enable four DMA buffers.
 * @details No need to configure RX buffers.
 * @return none.
*******************************************************************************/
void ECAN1TxRxBuffersConfig(void)
{
    C1CTRL1bits.WIN = 0;
    C1TR01CONbits.TXEN0 = 1;
}

/***************************************************************************//**
 * @brief Enable 2 filters. The ID are defined in node.h
 * @details Note that using Register Indirect mode does not affect the filter
 *          and masking capability. You may not want to set up the CxBUFPNTn
 *          bits to point to FIFO. This way you can avoid the FIFO interrupts.
 *          Just point to an arbitrary RX buffer.
 * @return none.
*******************************************************************************/
void ECAN1RxFiltersConfig(void )
{
    /* In this case filters 0 and 4 are used */

     C1CTRL1bits.WIN = 1;
     C1FEN1 = 0x11;

     C1RXF0SID = ECANFilterSID(SID1,EID1,1);
     C1RXF0EID = ECANFilterEID(EID1);

     C1RXF4SID = ECANFilterSID(SID2,EID2,1);
     C1RXF4EID =ECANFilterEID(EID2);

     C1RXM0SID = 0xFFEB;		/* Configure MASK 0 - All bits used in comparison*/
     C1RXM0EID = 0xFFFF;

     C1FMSKSEL1bits.F0MSK = 0x0;	/* User MASK 0 for all filters */
     C1FMSKSEL1bits.F4MSK = 0x0;	/* User MASK 0 for all filters */

     C1BUFPNT1bits.F0BP = 0x1;		/* Set the destination buffers to be any thing but */
     C1BUFPNT2bits.F4BP = 0x1;		/* configured transmit buffers */
}


/***************************************************************************//**
 * @brief The TXREQ bit is set. This will make the ECAN module generate request to the DMA.
 * @details Note that in Register Indirect Mode the ECAN module is
 *          not aware of buffers. So setting the TXREQ bit will cause data be
 *          transferred from the location pointed by the DMA.
 *          Just point to an arbitrary RX buffer.
 * @return none.
*******************************************************************************/
int ECAN1SendPacket(void)
{
    /* The DMA Pointer will have to be reset in case
    * arbitration is lost. This can be done by checking
    * the TXLARB bit in the CxTRmnCON register. The DMA
    * Pointer is reset by disabling and enabling
    * the DMA channel.*/

    /* If the message transmission is aborted the function
    * will return a zero. */

    C1TR01CONbits.TXREQ0 = 1;
    while(C1TR01CONbits.TXREQ0 == 1)
    {
        if(C1TR01CONbits.TXLARB0 == 1)
        {
                /* Arbitration lost. Abort the
                 * message and reset the transmit
                 * buffer DMA */

                C1TR01CONbits.TXREQ0 = 0;
                DMA0CONbits.CHEN = 0;
                DMA0CONbits.CHEN = 1;
                C1TR01CONbits.TXREQ0 = 1;
        }
    }

    if(C1TR01CONbits.TXABT0 == 1)
    {
        return(0);
    }

    return(1);			
}	

/***************************************************************************//**
 * @brief equest the normal operational mode and wait till it is set.
 * @return none.
*******************************************************************************/
void ECAN1SetOPMode(void)
{
    C1CTRL1bits.REQOP = 0;
    while(C1CTRL1bits.OPMODE != 0);
}
/***************************************************************************//**
 * @brief This function will create a standard ID packet DLC is set to 8.
 * @param data - points to the data payload
 * @param SID - Standard ID value to be used.
 * @param output - points to where the packet should be stored which typically is an ECAN TX buffer
 * @return none.
*******************************************************************************/
void ECANCreateSIDPacket(int * data, int sid,int * output)
{
     output[0] = (sid << 2) & 0x1FFC;		/* SID, SRR = 0 and IDE = 0 */
     output[1] = 0x8;
     output[2] = data[0];
     output[3] = data[1];
     output[4] = data[2];
     output[5] = data[3];
     output[6] = 0;
}  

/***************************************************************************//**
 * @brief This function will create a extended ID packet DLC is set to 8.
 * @param data - points to the data payload
 * @param SID - Standard ID value to be used.
 * @param EID - Extended ID value to be used.
 * @param output - points to where the packet should be stored which typically is an ECAN TX buffer
 * @return none.
*******************************************************************************/
void ECANCreateEIDPacket(int * data,int sid,long eid,__eds__ int * output)
{	 
     output[0] = (sid << 2) | 0x3;
     output[1] = (int)(eid >> 6) & 0x0FFF;
     output[2] = ((int)(eid & 0x3F) << 10) | 0x8;
     output[3] = data[0];
     output[4] = data[1];
     output[5] = data[2];
     output[6] = data[3];
}
                         
