/**********************************************************************
* $Id$		lpc18xx_can.c		2011-06-02
*//**
* @file		lpc18xx_can.c
* @brief	Contains all functions support for C CAN firmware library
* 			on LPC18xx
* @version	1.0
* @date		02. June. 2011
* @author	NXP MCU SW Application Team
*
* Copyright(C) 2011, NXP Semiconductor
* All rights reserved.
*
***********************************************************************
* Software that is described herein is for illustrative purposes only
* which provides customers with programming information regarding the
* products. This software is supplied "AS IS" without any warranties.
* NXP Semiconductors assumes no responsibility or liability for the
* use of the software, conveys no license or title under any patent,
* copyright, or mask work right to the product. NXP Semiconductors
* reserves the right to make changes in the software without
* notification. NXP Semiconductors also make no representation or
* warranty that such application will be suitable for the specified
* use without further testing or modification.
**********************************************************************/

/* Peripheral group ----------------------------------------------------------- */
/** @addtogroup C_CAN
 * @{
 */

/* Includes ------------------------------------------------------------------- */
#include "LPC18xx.h"
#include "lpc18xx_can.h"
#include "lpc18xx_cgu.h"

/* If this source file built with example, the LPC18xx FW library configuration
 * file in each example directory ("lpc18xx_libcfg.h") must be included,
 * otherwise the default FW library configuration file must be included instead
 */
#ifdef __BUILD_WITH_EXAMPLE__
#include "lpc18xx_libcfg.h"
#else
#include "lpc18xx_libcfg_default.h"
#endif /* __BUILD_WITH_EXAMPLE__ */

#ifdef _C_CAN

/* Private Macros ---------------------------------------------------------- */
#ifndef __GNUC__
/* Macro for reading and writing to CCAN IF registers */
#define CAN_IF_Read(reg, IFsel) (LPC_C_CAN0->##IFsel##_##reg)
#define CAN_IF_Write(reg, IFsel, val) (LPC_C_CAN0->##IFsel##_##reg=val)

/* Macro for writing IF to specific RAM message object */
#define CAN_IF_readBuf(IFsel,msg) \
  LPC_C_CAN0->##IFsel##_##CMDMSK_W=RD|MASK|ARB|CTRL|CLRINTPND|DATAA|DATAB; \
  LPC_C_CAN0->##IFsel##_##CMDREQ=msg; \
  while (LPC_C_CAN0->##IFsel##_##CMDREQ & IFCREQ_BUSY );

/* Macro for reading specific RAM message object to IF */
#define CAN_IF_writeBuf(IFsel,msg) \
  LPC_C_CAN0->##IFsel##_##CMDMSK_W=WR|MASK|ARB|CTRL|CLRINTPND|DATAA|DATAB; \
  LPC_C_CAN0->##IFsel##_##CMDREQ=msg; \
  while (LPC_C_CAN0->##IFsel##_##CMDREQ & IFCREQ_BUSY );
#else
#define CAN_IF_Read(reg, IFsel) (LPC_C_CAN0->IFsel##_##reg)
#define CAN_IF_Write(reg, IFsel, val) (LPC_C_CAN0->IFsel ## _ ## reg = val)

/* Macro for writing IF to specific RAM message object */
#define CAN_IF_readBuf(IFsel,msg) \
  LPC_C_CAN0->IFsel##_##CMDMSK_W=RD|MASK|ARB|CTRL|CLRINTPND|DATAA|DATAB; \
  LPC_C_CAN0->IFsel##_##CMDREQ=msg; \
  while (LPC_C_CAN0->IFsel##_##CMDREQ & IFCREQ_BUSY );

/* Macro for reading specific RAM message object to IF */
#define CAN_IF_writeBuf(IFsel,msg) \
  LPC_C_CAN0->IFsel##_##CMDMSK_W=WR|MASK|ARB|CTRL|CLRINTPND|DATAA|DATAB; \
  LPC_C_CAN0->IFsel##_##CMDREQ=msg; \
  while (LPC_C_CAN0->IFsel##_##CMDREQ & IFCREQ_BUSY );
#endif

#define IF1	0
#define IF2	1

#define CAN_STATUS_INTERRUPT      0x8000    /* 0x0001-0x0020 are the # of the message
										    object */
                                            /* 0x8000 is the status interrupt */

/* CAN Message interface register definitions */
/* bit field of IF command request n register */
#define IFCREQ_BUSY               0x8000    /* 1 is writing is progress, cleared when
                                            RD/WR done */
/* CAN CTRL register */
#define CTRL_INIT		(1 << 0)
#define CTRL_IE			(1 << 1)
#define CTRL_SIE		(1 << 2)
#define CTRL_EIE		(1 << 3)
#define CTRL_DAR		(1 << 5)
#define CTRL_CCE		(1 << 6)
#define CTRL_TEST		(1 << 7)

/* CAN Test register */
#define TEST_BASIC		(1 << 2)
#define TEST_SILENT		(1 << 3)
#define TEST_LBACK		(1 << 4)

/* CAN Status register */
#define STAT_LEC		(0x7 << 0)
#define STAT_TXOK		(1 << 3)
#define STAT_RXOK		(1 << 4)
#define STAT_EPASS		(1 << 5)
#define STAT_EWARN		(1 << 6)
#define STAT_BOFF		(1 << 7)

#define NO_ERR		0 	// No Error
#define STUFF_ERR	1	// Stuff Error : More than 5 equal bits in a sequence have occurred in a part
						// of a received message where this is not allowed.
#define FORM_ERR	2	// Form Error : A fixed format part of a received frame has the wrong format.
#define ACK_ERR		3 	// AckError : The message this CAN Core transmitted was not acknowledged
						// by another node.
#define BIT1_ERR	4 	// Bit1Error : During the transmission of a message (with the exception of
						// the arbitration field), the device wanted to send a recessive level (bit of
						// logical value ‘1’), but the monitored bus value was dominant.
#define BIT0_ERR	5 	// Bit0Error : During the transmission of a message (or acknowledge bit,
						// or active error flag, or overload flag), the device wanted to send a
						// LOW/dominant level (data or identifier bit logical value ‘0’), but the
						// monitored Bus value was HIGH/recessive. During busoff recovery this
						// status is set each time a
						// sequence of 11 HIGH/recessive bits has been monitored. This enables
						// the CPU to monitor the proceeding of the busoff recovery sequence
						// (indicating the bus is not stuck at LOW/dominant or continuously
						// disturbed).
#define CRC_ERR		6 	// CRCError: The CRC checksum was incorrect in the message received.


/* bit field of IF command mask register */
#define	DATAB		(1 << 0)   /* 1 is transfer data byte 4-7 to message object, 0 is not */
#define	DATAA		(1 << 1)   /* 1 is transfer data byte 0-3 to message object, 0 is not */
#define	NEWDAT		(1 << 2)   /* Clear NEWDAT bit in the message object */
#define	CLRINTPND	(1 << 3)
#define	CTRL		(1 << 4)   /* 1 is transfer the CTRL bit to the message object, 0 is not */
#define	ARB			(1 << 5)   /* 1 is transfer the ARB bits to the message object, 0 is not */
#define	MASK		(1 << 6)   /* 1 is transfer the MASK bit to the message object, 0 is not */
#define	WR			(1 << 7)   /* 0 is READ, 1 is WRITE */
#define RD      	0x0000

/* bit field of IF mask 2 register */
#define	MASK_MXTD	(1 << 15)     /* 1 extended identifier bit is used in the RX filter unit, 0 is not */
#define	MASK_MDIR	(1 << 14)     /* 1 direction bit is used in the RX filter unit, 0 is not */

/* bit field of IF identifier 2 register */
#define	ID_MVAL		(1 << 15)     /* Message valid bit, 1 is valid in the MO handler, 0 is ignored */
#define	ID_MTD		(1 << 14)     /* 1 extended identifier bit is used in the RX filter unit, 0 is not */
#define	ID_DIR		(1 << 13)     /* 1 direction bit is used in the RX filter unit, 0 is not */

/* bit field of IF message control register */
#define	NEWD		(1 << 15)     /* 1 indicates new data is in the message buffer.  */
#define	MLST		(1 << 14)     /* 1 indicates a message loss. */
#define	INTP		(1 << 13)     /* 1 indicates message object is an interrupt source */
#define UMSK    	(1 << 12)     /* 1 is to use the mask for the receive filter mask. */
#define	TXIE		(1 << 11)     /* 1 is TX interrupt enabled */
#define	RXIE		(1 << 10)     /* 1 is RX interrupt enabled */

#if REMOTE_ENABLE
	#define	RMTEN		(1 << 9)  /* 1 is remote frame enabled */
#else
	#define	RMTEN		0
#endif

#define TXRQ    	(1 << 8)      /* 1 is TxRqst enabled */
#define	EOB			(1 << 7)      /* End of buffer, always write to 1 */
#define	DLC			0x000F        /* bit mask for DLC */

#define ID_STD_MASK		0x07FF
#define ID_EXT_MASK		0x1FFFFFFF
#define DLC_MASK		0x0F

/* Private Variables ---------------------------------------------------------- */
/* Statistics of all the interrupts */
/* Buss off status counter */
volatile uint32_t BOffCnt = 0;
/* Warning status counter. 	At least one of the error counters
 in the EML has reached the error warning limit of 96 */
volatile uint32_t EWarnCnt = 0;
/* More than 5 equal bits in a sequence in received message */
volatile uint32_t StuffErrCnt = 0;
/* Wrong format of fixed format part of a received frame */
volatile uint32_t FormErrCnt = 0;
/* Transmitted message not acknowledged. */
volatile uint32_t AckErrCnt = 0;
/* Send a HIGH/recessive level, but monitored LOW/dominant */
volatile uint32_t Bit1ErrCnt = 0;
/* Send a LOW/dominant level, but monitored HIGH/recessive */
volatile uint32_t Bit0ErrCnt = 0;
/* The CRC checksum was incorrect in the message received */
volatile uint32_t CRCErrCnt = 0;
/* Message object new data error counter */
volatile uint32_t ND1ErrCnt = 0;

MSG_CB TX_cb, RX_cb;

message_object can_buff[CAN_MSG_OBJ_MAX];
message_object recv_buff;

#if CAN_DEBUG
uint32_t CANStatusLog[100];
uint32_t CANStatusLogCount = 0;
#endif

//#ifdef __GNUC__
//uint32_t CAN_IF_Read(uint32_t reg,uint32_t IFsel){
//	if(IFsel == IF1){
//		return (LPC_C_CAN0->IF1_reg);
//	}else{
//		return (LPC_C_CAN0->IF2_reg);
//	}
//}
//void CAN_IF_Write(uint32_t reg, uint32_t IFsel,uint32_t val){
//	if(IFsel == IF1){
//	(LPC_C_CAN0->IF1_reg=val);
//	}else{
//		(LPC_C_CAN0->IF2_reg=val);
//	}
//}
//
///* Macro for writing IF to specific RAM message object */
//void CAN_IF_readBuf(uint32_t IFsel,uint32_t msg){
//	if(IFsel == IF1){
//	LPC_C_CAN0->IF1_CMDMSK_W=RD|MASK|ARB|CTRL|CLRINTPND|DATAA|DATAB;
//	LPC_C_CAN0->IF1_CMDREQ=msg;
//	while (LPC_C_CAN0->IF1_CMDREQ & IFCREQ_BUSY );
//	}else{
//	  LPC_C_CAN0->IF2_CMDMSK_W=RD|MASK|ARB|CTRL|CLRINTPND|DATAA|DATAB;
//	  LPC_C_CAN0->IF2_CMDREQ=msg;
//	  while (LPC_C_CAN0->IF2_CMDREQ & IFCREQ_BUSY );
//  }
//
//}
//
///* Macro for reading specific RAM message object to IF */
//void CAN_IF_writeBuf(uint32_t IFsel,uint32_t msg){
//	if(IFsel == IF1){
//  LPC_C_CAN0->IF1_CMDMSK_W=WR|MASK|ARB|CTRL|CLRINTPND|DATAA|DATAB;
//  LPC_C_CAN0->IF1_CMDREQ=msg;
//  while (LPC_C_CAN0->IF1_CMDREQ & IFCREQ_BUSY );
//	}else{
//		  LPC_C_CAN0->IF2_CMDMSK_W=WR|MASK|ARB|CTRL|CLRINTPND|DATAA|DATAB;
//		  LPC_C_CAN0->IF2_CMDREQ=msg;
//		  while (LPC_C_CAN0->IF2_CMDREQ & IFCREQ_BUSY );
//	}
//}
//#endif

/*********************************************************************//**
 * @brief		Handle valid received message
 * @param[in]	msg_no Message Object number
 * @return 		None
 **********************************************************************/
void CAN_RxInt_MessageProcess( uint8_t msg_no )
{
	uint32_t msg_id;
	uint32_t *p_add;
	uint32_t reg1, reg2;

	/* Import message object to IF2 */
 	CAN_IF_readBuf(IF2, msg_no);					/* Read the message into the IF registers */

	p_add = (uint32_t *)&recv_buff;

	if( CAN_IF_Read(ARB2, IF2) & ID_MTD )			/* bit 28-0 is 29 bit extended frame */
	{
		/* mask off MsgVal and Dir */
		reg1 = CAN_IF_Read(ARB1, IF2);
		reg2 = CAN_IF_Read(ARB2, IF2);
		msg_id = (reg1|(reg2<<16));
	}
	else
	{
		/* bit 28-18 is 11-bit standard frame */
		msg_id = (CAN_IF_Read(ARB2, IF2) &0x1FFF) >> 2;
	}

	p_add[0] = msg_id;
	p_add[1] = CAN_IF_Read(MCTRL, IF2) & 0x000F;	/* Get Msg Obj Data length */
	p_add[2] = (CAN_IF_Read(DA2, IF2)<<16) | CAN_IF_Read(DA1, IF2);
	p_add[3] = (CAN_IF_Read(DB2, IF2)<<16) | CAN_IF_Read(DB1, IF2);

	/* Clear interrupt pending bit */
	CAN_IF_Write(MCTRL, IF2, UMSK|RXIE|EOB|CAN_DLC_MAX);
	/* Save changes to message RAM */
	CAN_IF_writeBuf(IF2, msg_no);

	return;
}
/*********************************************************************//**
 * @brief		Handle valid transmit message
 * @param[in]	msg_no Message Object number
 * @return 		None
 **********************************************************************/
void CAN_TxInt_MessageProcess( uint8_t msg_no )
{
	/* Clear interrupt pending bit */
	CAN_IF_Write(MCTRL, IF2, UMSK|RXIE|EOB|CAN_DLC_MAX);
	/* Save changes to message RAM */
	CAN_IF_writeBuf(IF2,msg_no);
	return;
}

/*********************************************************************//**
 * @brief		CAN interrupt handler
 * @param[in]	None
 * @return 		None
 **********************************************************************/
volatile uint32_t nd_tmp;
void CAN_IRQHandler(void)
{
	uint32_t canstat = 0;
	uint32_t can_int, msg_no;

	while ( (can_int = LPC_C_CAN0->INT) != 0 )	/* While interrupt is pending */
	{
		canstat = LPC_C_CAN0->STAT;				/* Read CAN status register */

		if ( can_int & CAN_STATUS_INTERRUPT )
		{
			/* Passive state monitored frequently in main. */

			if ( canstat & STAT_EWARN )
			{
				EWarnCnt++;
				return;
			}
			if ( canstat & STAT_BOFF )
			{
				BOffCnt++;
				return;
			}

			switch (canstat&STAT_LEC)	/* LEC Last Error Code (Type of the last error to occur on the CAN bus) */
			{
				case NO_ERR:
					break;
				case STUFF_ERR:
					StuffErrCnt++;
					break;
				case FORM_ERR:
					FormErrCnt++;
					break;
				case ACK_ERR:
					AckErrCnt++;
					break;
				case BIT1_ERR:
					Bit1ErrCnt++;
					break;
				case BIT0_ERR:
					Bit0ErrCnt++;
					break;
				case CRC_ERR:
					CRCErrCnt++;
					break;
				default:
					break;
			}

			/* Clear all warning/error states except RXOK/TXOK */
			LPC_C_CAN0->STAT &= STAT_RXOK|STAT_TXOK;
		}
		else
		{
			if ( (canstat & STAT_LEC) == 0 ) 	/* NO ERROR */
			{
				msg_no = can_int & 0x7FFF;
				if((msg_no >= 1 ) && (msg_no <= 16))
				{
					LPC_C_CAN0->STAT &= ~STAT_RXOK;
					/* Check if message number is correct by reading NEWDAT registers.
					 By reading out the NEWDAT bits, the CPU can check for which Message
					 Object the data portion was updated
					 Only first 16 message object used for receive : only use ND1 */
					if((1<<(msg_no-1)) != LPC_C_CAN0->ND1)
					{
						/* message object does not contain new data! */
						ND1ErrCnt++;
						break;
					}
					CAN_RxInt_MessageProcess(msg_no);
					RX_cb(msg_no);
				}
				else
				{
				 	LPC_C_CAN0->STAT &= ~STAT_TXOK;
					CAN_TxInt_MessageProcess(msg_no);
					TX_cb(msg_no);
				}
			}
		}
	}
	return;
}

/*********************************************************************//**
 * @brief		Initialize CAN peripheral
 * @param[in]	BitClk CAN bit clock setting
 * @param[in]	ClkDiv CAN bit clock setting
 * @param[in]	Tx_cb point to call-back function when transmitted
 * @param[in]	Rx_cb point to call-back function when received
 * @return 		None
 **********************************************************************/
void CAN_Init( uint32_t BitClk, CCAN_CLKDIV_Type ClkDiv , MSG_CB Tx_cb, MSG_CB Rx_cb)
{

	RX_cb = Rx_cb;
	TX_cb = Tx_cb;
	if ( !(LPC_C_CAN0->CNTL & CTRL_INIT) )
	{
		/* If it's in normal operation already, stop it, reconfigure
		 everything first, then restart.  */
		LPC_C_CAN0->CNTL |= CTRL_INIT;	/* Default state */
	}

	LPC_C_CAN0->CLKDIV = ClkDiv;			/* Divider for CAN VPB3 clock */
	LPC_C_CAN0->CNTL |= CTRL_CCE;		/* Start configuring bit timing */
	LPC_C_CAN0->BT = BitClk;
	LPC_C_CAN0->BRPE = 0x0000;
	LPC_C_CAN0->CNTL &= ~CTRL_CCE;		/* Stop configuring bit timing */

	LPC_C_CAN0->CNTL &= ~CTRL_INIT;		/* Initialization finished, normal operation now. */
	while ( LPC_C_CAN0->CNTL & CTRL_INIT );

	/* By default, auto TX is enabled, enable all related interrupts */
	LPC_C_CAN0->CNTL |= (CTRL_IE|CTRL_SIE|CTRL_EIE);
	return;
}

/*********************************************************************//**
 * @brief		Send a message to the CAN port
 * @param[in]	msg_no message object number
 * @param[in]	msg_ptr msg buffer pointer
 * @return 		None
 **********************************************************************/
void CAN_Send(uint8_t msg_no, uint32_t *msg_ptr )
{
	uint32_t tx_id, Length;

	if(msg_ptr == NULL) return;

	/* first is the ID, second is length, the next four are data */
	tx_id = *msg_ptr++;
	Length = *msg_ptr++;

	if(Length>CAN_DLC_MAX)Length = CAN_DLC_MAX;
	CAN_IF_Write(MCTRL, IF1, UMSK|TXIE|TXRQ|EOB|RMTEN|(Length & DLC_MASK));
	CAN_IF_Write(DA1, IF1, *msg_ptr);			/* Lower two bytes of message pointer */
	CAN_IF_Write(DA2, IF1, (*msg_ptr++)>>16);	/* Upper two bytes of message pointer */
	CAN_IF_Write(DB1, IF1, *msg_ptr);			/* Lower two bytes of message pointer */
	CAN_IF_Write(DB2, IF1, (*msg_ptr)>>16);		/* Upper two bytes of message pointer */

	/* Configure arbitration */
	if(!(tx_id & (0x1<<30)))					/* bit 30 is 0, standard frame */
	{
		/* Mxtd: 0, Mdir: 1, Mask is 0x7FF */
		CAN_IF_Write(MSK2, IF1, MASK_MDIR | (ID_STD_MASK << 2));
		CAN_IF_Write(MSK1, IF1, 0x0000);

		/* MsgVal: 1, Mtd: 0, Dir: 1, ID = 0x200 */
		CAN_IF_Write(ARB1, IF1, 0x0000);
		CAN_IF_Write(ARB2, IF1, ID_MVAL| ID_DIR | (tx_id << 2));
	}
	else										/* Extended frame */
	{
		/* Mxtd: 1, Mdir: 1, Mask is 0x7FF */
		CAN_IF_Write(MSK2, IF1, MASK_MXTD | MASK_MDIR | (ID_EXT_MASK >> 16));
		CAN_IF_Write(MSK1, IF1, ID_EXT_MASK & 0x0000FFFF);

		/* MsgVal: 1, Mtd: 1, Dir: 1, ID = 0x200000 */
		CAN_IF_Write(ARB1, IF1, tx_id & 0x0000FFFF);
		CAN_IF_Write(ARB2, IF1, ID_MVAL|ID_MTD | ID_DIR | (tx_id >> 16));
	}

  	/* Write changes to message RAM */
	CAN_IF_writeBuf(IF1, msg_no);

	return;
}

/*********************************************************************//**
 * @brief		Listen for a message on CAN bus
 * @param[in]	msg_no message object number
 * @param[in]	msg_ptr msg buffer pointer
 * @param[in]	RemoteEnable Enable/disable remote frame support, should be:
 * 					- TRUE:	 enable
 * 					- FALSE: disable
 * @return 		None
 **********************************************************************/
void CAN_Recv(uint8_t msg_no, uint32_t *msg_ptr, Bool RemoteEnable)
{
	uint32_t rx_id = *msg_ptr;
	uint32_t rmten = 0;
	if(RemoteEnable){
		rmten = 1<<8;
	}
	if(!(rx_id & (0x1<<30))){ /* standard frame */

 		/* Mxtd: 0, Mdir: 0, Mask is 0x7FF */
	  	CAN_IF_Write(MSK1, IF1, 0x0000);
		CAN_IF_Write(MSK2, IF1, ID_STD_MASK << 2);
		/* MsgVal: 1, Mtd: 0, Dir: 0 */
		CAN_IF_Write(ARB1, IF1, 0x0000);
		CAN_IF_Write(MCTRL, IF1, rmten|UMSK|RXIE|EOB|CAN_DLC_MAX);
		CAN_IF_Write(DA1, IF1, 0x0000);
		CAN_IF_Write(DA2, IF1, 0x0000);
		CAN_IF_Write(DB1, IF1, 0x0000);
		CAN_IF_Write(DB2, IF1, 0x0000);
		CAN_IF_Write(ARB2, IF1, ID_MVAL | ((rx_id) << 2));
		/* Transfer data to message RAM */
		CAN_IF_writeBuf(IF1, msg_no);
	}

	else{
		rx_id &= (0x1<<30)-1 ; /* Mask ID bit */
		/* Mxtd: 1, Mdir: 0, Mask is 0x1FFFFFFF */
		CAN_IF_Write(MSK1, IF1, ID_EXT_MASK & 0xFFFF);
		CAN_IF_Write(MSK2, IF1, MASK_MXTD | (ID_EXT_MASK >> 16));
		/* MsgVal: 1, Mtd: 1, Dir: 0 */
		CAN_IF_Write(ARB1, IF1, (rx_id) & 0xFFFF);
		CAN_IF_Write(MCTRL, IF1, rmten|UMSK|RXIE|EOB|CAN_DLC_MAX);
		CAN_IF_Write(DA1, IF1, 0x0000);
		CAN_IF_Write(DA2, IF1, 0x0000);
		CAN_IF_Write(DB1, IF1, 0x0000);
		CAN_IF_Write(DB2, IF1, 0x0000);
		CAN_IF_Write(ARB2, IF1, ID_MVAL | ID_MTD | ((rx_id) >> 16));
		/* Transfer data to message RAM */
		CAN_IF_writeBuf(IF1, msg_no);
	}
	return;
}

/*********************************************************************//**
 * @brief		Read a message from Message RAM to buffer
 * @param[in]	msg_no message object number
 * @param[in]	buff msg buffer pointer
 * @return 		None
 **********************************************************************/
void CAN_ReadMsg(uint32_t msg_no, message_object* buff){
	int i;
	buff->id = recv_buff.id;
	buff->dlc = recv_buff.dlc;
	if(recv_buff.dlc>CAN_DLC_MAX) recv_buff.dlc = CAN_DLC_MAX;
	for(i=0;i<recv_buff.dlc;i++)
		buff->data[i] = recv_buff.data[i];
}

#endif /* _C_CAN*/
/**
 * @}
 */

/* --------------------------------- End Of File ------------------------------ */


