/*********************************************************************
 *
 *              LIN-Master Software
 *
 *********************************************************************
 * FileName:        ELINMInt.c
 * Dependencies:    ELINMInt.h
 * Processor:       18Fxxxx
 * Compilers:	    Microchip C Compiler -> mcc C18 v2.20 or better
 * Assembler:       MPASMWIN 02.70.02 or higher
 * Linker:          MPLINK 2.33.00 or higher
 * Company:         Microchip Technology, Inc.
 *
 * Software License Agreement
 *
 * The software supplied herewith by Microchip Technology Incorporated
 * (the "Company") is intended and supplied to you, the Companys
 * customer, for use solely and exclusively with products manufactured
 * by the Company. 
 *
 * The software is owned by the Company and/or its supplier, and is 
 * protected under applicable copyright laws. All rights are reserved. 
 * Any use in violation of the foregoing restrictions may subject the 
 * user to criminal sanctions under applicable laws, as well as to 
 * civil liability for the breach of the terms and conditions of this 
 * license.
 
 * THIS SOFTWARE IS PROVIDED IN AN "AS IS" CONDITION. NO WARRANTIES, 
 * WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT NOT LIMITED 
 * TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
 * PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. THE COMPANY SHALL NOT, 
 * IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL OR 
 * CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
 *
 *
 * This FW provides the necessary functions to create a Master LIN 
 * node driver using an EUSART enabled 18F part.
 *
 * Author               Date            Comment
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * CG			09/4/03	        Initial Release (Maestro 1.0)
 *
 ********************************************************************/

#define    _LINM18ESOURCE

#include "ELINMInt.h"


BYTE _ELINMIntMessageTag;					        // message tag -> identifies one specific message
ELINMINT_CRC _ELINMIntRXCRC;					        // reception CRC buffer
ELINMINT_STATUS _ELINMIntStatus;                                        // main status byte
ELINMINT_STATUS1 _ELINMIntStatus1;				        // second status byte
ELINMINT_MESSAGE_SIZE _ELINMIntMessageSize;		        	// message size
ELINMINT_MESSAGE_SIZE _ELINMIntRXMessageSize;		                // reception message size
BYTE _ELINMIntMessageBuffer[ELINMINT_MAX_MESSAGE_SIZE+3];       	// message buffer
BYTE _ELINMIntReadBack;					        	// saves the last transmitted byte to be compared with feedback

BYTE _ELINMIntMessageBufferPointer;				        // TX byte position pointer
char _ELINMIntSpace;                                    	        // interbyte and interframe space
unsigned int _ELINMIntTFrameMin;				        // timing control variables
unsigned int _ELINMIntTFrameMax;
unsigned int _ELINMIntTHeaderMin;
unsigned int _ELINMIntTHeaderMax;
unsigned long _ELINMIntSleepTimeout;                                    // sleep time-out count variable


/*********************************************************************
 * Function:    BYTE _ELINMIntInitialize(void)
 *
 * PreCondition:
 *
 * Input:

 * Output:      0=NO Error
 *              !0=ERROR CODE
 *
 * Side Effects:
 *
 * Stack Requirements:
 *
 * Overview:	This procedure Initializes:
 *                      The Baud Rate
 *                      Reception Interrupt (enabled)
 *                      Protocol Status bytes - > put protocol in IDLE 
 *                      Interbyte timing
 *                      
 ********************************************************************/
BYTE _ELINMIntInitialize(void)
{
 SPBRG 	=ELINMINT_SPBRG;				        	// set Baud Rate (16 bits)
 SPBRGH =ELINMINT_SPBRGH;
 TXSTA 	=ELINMINT_TXSTA_INIT;					        // initiates transmission and reception
 RCSTA	=ELINMINT_RCSTA_INIT;
 BAUDCON=ELINMINT_BAUDCON_INIT;
 PIE1	|=ELINMINT_PIE1_INIT;			        	        // 
 _ELINMIntSleepTimeout=ELINMINT_SLEEP_TIMEOUT;                          // init bus idle-to-sleep timing
 _ELINMIntSpace=ELINMINT_INTERBYTE_SPACE;		                // start interbyte space
 _ELINMIntStatus.ELINMIntStatusByte=0;                                  // clear status
 _ELINMIntStatus1.ELINMIntStatusByte=0;                                 // clear status1
 _ELINMIntStatus.ELINMINTSTS.IDLE=1;                                    // return to idle state
 return(0);                                                             // return 0 - OK 
}

/*********************************************************************
 * Function:    void _ELINMIntResetProtocol(BYTE code)
 *
 * PreCondition:
 *
 * Input:	BYTE code - any eventual error code
 *					
 * Output:			
 *
 * Side Effects:
 *
 * Stack Requirements:
 *
 * Overview:	This procedure resets the protocol and if necessary 
 * 		sets the error flags
 *
 ********************************************************************/
void _ELINMIntResetProtocol(BYTE code)
{
 _ELINMIntReadBack=RCREG;	        		                // read the receive register to clear RCIF flag
 _ELINMIntStatus1.ELINMIntStatusByte=0;                                 // reset all aux. status conditions including
                                                                        // sleep timeout
 _ELINMIntRXCRC.CRC=0;			        			// reset CRC 
 _ELINMIntStatus.ELINMIntStatusByte=code;	        		// status code set. 
 _ELINMIntSleepTimeout=ELINMINT_SLEEP_TIMEOUT;                          // init bus idle-to-sleep timing
}


/*********************************************************************
 * Function:    void interrupt ELINMIntHandler(void)
 *
 * PreCondition:	
 *
 * Input:

 * Output:
 *
 * Side Effects:
 *
 * Stack Requirements:
 *
 * Overview:	Interrupt Handler for EUSART based Master node LIN
 *		To be called by a timer based interrupt routine 		
 *
 ********************************************************************/

void ELINMIntHandler(void)
{
 if(TXSTA_SENDB==0)     				                // check if it is not sending break 
 {
  if(PIR1_RCIF)						                // also checks if received something	
  {
   if(_ELINMIntStatus.ELINMINTSTS.IDLE)			                // check if in IDLE state	
   {
    if((RCSTA&0x06)==0)				                        // if received without error
    {
     if(RCREG==ELINMINT_WAKEUP_BYTE)			                // then check if it was an wake-up request
     {						
      _ELINMIntSleepTimeout=ELINMINT_SLEEP_TIMEOUT;                     // reinit bus-idle-to sleep timing      
      _ELINMIntReadBack=RCREG;	        		                // read the receive register to clear RCIF flag                
      if(_ELINMIntStatus1.ELINMINTSTS.WAKEUP_SENT)                      // if an wake-up signal was sent from the master to the slave
       _ELINMIntStatus1.ELINMINTSTS.WAKEUP_SENT=0;                      // clear the flag
      else                                                              // if wake-up signal 
       _ELINMIntStatus1.ELINMINTSTS.WAKEUP=1;		                // set the wake-up flag
     } // else if(RCREG==ELINMINT_WAKEUP_BYTE)
    } // if((RCSTA&0x06)==0)
   } // if(_ELINMIntStatus.ELINMINTSTS.IDLE)
   else if(_ELINMIntStatus.ELINMINTSTS.TX)		                // check if transmitting
   {
    if((RCSTA&0x06)&&_ELINMIntMessageBufferPointer)	                // if error and it's not in the BREAK byte (pointer==0)
     _ELINMIntResetProtocol(ELINMINT_IDLEBIT+ELINMINT_ERRORBIT+ELINMINT_FRAMING_ERROR);	// signal the error
    else						                // if no error detected or error detected in BREAK (expected)
    {  
     if(_ELINMIntMessageBufferPointer==2)		                // then check if it just sent the HEADER
      _ELINMIntStatus1.ELINMINTSTS.HEADER=0;		                // if so turn-off the header transmission flag (it will trigger the check of header max. time)
     if(_ELINMIntSpace)					                // if interbyte space not zero decrement and go away
      _ELINMIntSpace--;
     else if(_ELINMIntReadBack!=RCREG)			                // otherwise check if the byte read is the same as the sent one
      _ELINMIntResetProtocol(ELINMINT_IDLEBIT+ELINMINT_ERRORBIT+ELINMINT_DATA_ERROR);	// if not signals the error
     else						                // if byte read is the same as the sent one
     {
      if(_ELINMIntMessageSize.SIZE)			                // and if there is still message to be sent
      {
       _ELINMIntReadBack=RCREG;	        		                // read the receive register to clear RCIF flag
       _ELINMIntReadBack=_ELINMIntMessageBuffer[_ELINMIntMessageBufferPointer];	// load next byte to be sent
       TXREG=_ELINMIntReadBack;				                // also load the read-back check byte
       _ELINMIntMessageSize.SIZE--;			                // decrement the counter
       _ELINMIntMessageBufferPointer++;         			// increment the pointer
       _ELINMIntSpace+=ELINMINT_INTERBYTE_SPACE;                        // start interbyte space counter
      }
      else								// if all message already transmitted
      {
       if(_ELINMIntStatus.ELINMINTSTS.RX)                		// and if it's set to receive
       {
        _ELINMIntStatus.ELINMINTSTS.TX=0;                                // then clear the transmission flag               
        _ELINMIntMessageBufferPointer=0;		                // and reset the pointer
       } 
       else															// however, if it's not going to receive
       {
        _ELINMIntStatus1.ELINMINTSTS.FRAME=0;		                // then clear frame check flag (force the max. frame time to be checked)
        _ELINMIntResetProtocol(ELINMINT_IDLEBIT);	                // reset the protocol without error
       } // else
      } // else
     } // else 
    } // else of "if((RCSTA&0x06)&&ELINMIntMessageBufferPointer)"
   } // else if(_ELINMIntStatus.ELINMINTSTS.TX)
   else if(_ELINMIntStatus.ELINMINTSTS.RX)		                // if receiving
   {
    if(RCSTA&0x06)					                // and if an error was detected
     _ELINMIntResetProtocol(ELINMINT_IDLEBIT+ELINMINT_ERRORBIT+ELINMINT_FRAMING_ERROR);	// reset protocol with error
    else 
    {
     if(_ELINMIntRXMessageSize.SIZE)			                // if no error detected and if there is still message to be received
     {
      _ELINMIntMessageBuffer[_ELINMIntMessageBufferPointer]=RCREG;	// load message data byte in the buffer
      _ELINMIntRXMessageSize.SIZE--;			                // decrement the counter
      _ELINMIntRXCRC.CRC+=RCREG;					// add to CRC      
      if(_ELINMIntRXCRC.CRCbits.CRC8)					// if a carry bit from the lower byte
       _ELINMIntRXCRC.CRCL++;						// add the carry bit 
      _ELINMIntMessageBufferPointer++;      				// increment pointer
     } // if(ELINMIntRXMessageSize.SIZE)
     else															// if all data bytes already received
     {
      _ELINMIntStatus1.ELINMINTSTS.FRAME==0;
      _ELINMIntRXCRC.CRCL += RCREG+1;					// check CRC(checksum) by XORing received CRC
      if(_ELINMIntRXCRC.CRCL)
       _ELINMIntResetProtocol(ELINMINT_IDLEBIT+ELINMINT_ERRORBIT+ELINMINT_CHECKSUM_ERROR); // if error reset protocol and signal error									// if result != 0 error!
      else
       _ELINMIntResetProtocol(ELINMINT_IDLEBIT);			// otherwise just reset protocol without error
     } // else
    } // else	
   } // else if(_ELINMIntStatus.ELINMINTSTS.RX)
  } // if(PIR1_RCIF)
 } // if(SENDB==0) 
 if(_ELINMIntStatus.ELINMINTSTS.IDLE==0) 
 {
//  _ELINMIntReadBack=RCREG;	        		                // read the receive register to clear RCIF flag                 
#if	ELINMINT_EXTENDED_MESSAGE==1					// if an extended error message in place
  if(_ELINMIntStatus1.ELINMINTSTS.EXTENDED==0)				// and extended ID not selected
  {
#endif 
   if(_ELINMIntStatus1.ELINMINTSTS.FRAME==1)				// check flag bit of transmission of frame	
   {
    if(_ELINMIntStatus1.ELINMINTSTS.HEADER==1)				// check flag bit of transmission of header	
    {
     if(_ELINMIntTHeaderMin)						// while the min. header time > 0 decrement it
      _ELINMIntTHeaderMin--;											
     if(_ELINMIntTHeaderMax)						// while the max. header time > 0 decrement it
      _ELINMIntTHeaderMax--;														
     else								// however, if max. header time == 0 and header transmission not completed
      _ELINMIntResetProtocol(ELINMINT_IDLEBIT+ELINMINT_ERRORBIT+ELINMINT_THMAX_ERROR);	// then reset with error
    }  
    else // if(_ELINMIntStatus1.ELINMINTSTS.HEADER==1)				// check flag bit of transmission of header	
    {
     if(_ELINMIntTHeaderMin)			        		// check header minimum time
     _ELINMIntResetProtocol(ELINMINT_IDLEBIT+ELINMINT_ERRORBIT+ELINMINT_THMIN_ERROR);	// if > 0 --> error 
    }
    if(_ELINMIntTFrameMin)						// while min. frame time > 0 decrement it
     _ELINMIntTFrameMin--;															
    if(_ELINMIntTFrameMax)						// while max. frame time > 0 decrement it		
     _ELINMIntTFrameMax--;														
    else								// however if the max. frame time ==0 and frame not completed
     _ELINMIntResetProtocol(ELINMINT_IDLEBIT+ELINMINT_ERRORBIT+ELINMINT_TFMAX_ERROR);	// signal error
   }
   else									// if frame transmission (or reception) completed
   {
    if(_ELINMIntTFrameMin)						// check frame min. time
     _ELINMIntResetProtocol(ELINMINT_IDLEBIT+ELINMINT_ERRORBIT+ELINMINT_TFMIN_ERROR);	// if >0 --> error
   }
#if	ELINMINT_EXTENDED_MESSAGE==1
  }
#endif 
 }
 else // if(_ELINMIntStatus.ELINMINTSTS.IDLE==0) 
 {
  if(_ELINMIntStatus1.ELINMINTSTS.WAKEUP_SENT==0)                       // once the wake-up process is initiated                       
  {                                                                     // the sleep timeout is blocked           
   if(_ELINMIntSleepTimeout)
    _ELINMIntSleepTimeout--;
   else
    _ELINMIntStatus1.ELINMINTSTS.SLEEP_TIMEOUT=1;
  }  
 }
} //function

/*********************************************************************
 * Function:    BYTE _ELINMIntCalcIDParity(ELINMINT_ID ELINM_idtr)
 *
 * PreCondition:
 *
 * Input:	An ID 
 *
 * Output:	The same ID with parity bits set
 *
 * Side Effects:    
 *
 * Stack Requirements:
 *
 * Overview:	This functions calculates the Parity and then sends the ID.
 *		It doesn't check if the TXREG is available according to the
 *		LIN spec.
 *
 ********************************************************************/
BYTE _ELINMIntCalcIDParity(ELINMINT_ID ELINM_idtr)
{
 ELINM_idtr.ID &= 0x3F;				                        // ensure parity bits are clean
 if(ELINM_idtr.IDbits.ID0)			                        // calculates the Parity bit 6
  ELINM_idtr.ID^=0x40;
 if(ELINM_idtr.IDbits.ID1)
  ELINM_idtr.ID^=0x40;
 if(ELINM_idtr.IDbits.ID2)
  ELINM_idtr.ID^=0x40;
 if(ELINM_idtr.IDbits.ID4)
  ELINM_idtr.ID^=0x40;
 ELINM_idtr.IDbits.ID7=1;				                // calculates the Parity bit 7
 if(ELINM_idtr.IDbits.ID1)
  ELINM_idtr.ID^=0x80;
 if(ELINM_idtr.IDbits.ID3)
  ELINM_idtr.ID^=0x80;
 if(ELINM_idtr.IDbits.ID4)
  ELINM_idtr.ID^=0x80;
 if(ELINM_idtr.IDbits.ID5)
  ELINM_idtr.ID^=0x80;
 return((BYTE)ELINM_idtr.ID);
}
/*********************************************************************
 * Function: 	void _ELINMIntSendMessage(BYTE _ELINM_idr,char _ELINM_size,unsigned 
 *                                        int _ELINM_fmin,unsigned int _ELINM_fmax)
 *
 * PreCondition:ELINMIntInitialize invoked
 *              TX Buffer Available (using mELINMIntTXBufferAvailable())
 *
 * Input: 	The ID of the message
 *		The size of the message
 *		The minimum frame time
 *		The maximum frame time
 *
 * Output:
 *
 * Side Effects:Error Code is reset
 *
 * Stack Requirements:
 *
 * Overview:	This function sends a message. It is not to be invoked
 *              by the user directly but through the mELINMIntSendMessage
 *              macro, which calculates the frame timing.
 *
 ********************************************************************/
void _ELINMIntSendMessage(BYTE _ELINM_idr,char _ELINM_size,unsigned int _ELINM_fmin,unsigned int _ELINM_fmax)
{
 char _ELINM_i;
 ELINMINT_ID _ELINM_tid;
 unsigned int _ELINM_chk;
 

 _ELINM_tid.ID=_ELINM_idr;
 _ELINMIntStatus.ELINMIntStatusByte&=0x0F;                              // reset error codes (High Nibble of the byte)
                                                                        // the low nibble contains the state (IDLE, TX,....)
 _ELINMIntTHeaderMin = ELINMINT_THEADER_MIN;				// initialize min/max time for Header Variables
 _ELINMIntTHeaderMax = ELINMINT_THEADER_MAX;
 _ELINMIntTFrameMin=_ELINM_fmin;					// initialize min/max time for Frame Variables 						// the same for the entire frame
 _ELINMIntTFrameMax=_ELINM_fmax;

// if the user wants to use extended message the it must set the (ELINMINT_EXTENDED_MESSAGE==1)
// In this case a special EXTENDED flag is set when the extended frame message is used so
// that the normal Frame timing check is disabled. This is done because depending on the size
// of the extended message it will result in transmission times that cannot be handled by
// the unsigned int _ELINMIntTFrameMax and _ELINMIntTFrameMin variables.
// in this case the user must redefine these variables and measure the time/code overhead created

 if(_ELINMIntStatus.ELINMINTSTS.IDLE)                                   // if idle start
 {
#if ELINMINT_EXTENDED_MESSAGE==1					// if extended ID is going to be used
  if(_ELINM_idr==ELINMINT_EXTENDED_ID || _ELINM_idr==ELINMINT_EXTENDED_ID1)	// then check if in use and set proper flag
   _ELINMIntStatus1.ELINMINTSTS.EXTENDED=1;
#endif
  _ELINMIntMessageBuffer[0]=ELINMINT_SYNC_VALUE;			// load SYNC value
  _ELINMIntMessageBuffer[1]=_ELINMIntCalcIDParity(_ELINM_tid); 		// calculates ID's Parity
  _ELINMIntMessageBufferPointer=0;		        		// initiate pointer

// if reception is activated the number of bytes to be received is passed as a parameter so that the
// min. and max. frame times can be calculated, however the number of bytes to be transmitted must be 
// set to 2 so that only the sync and ID are sent.

  if(_ELINMIntStatus.ELINMINTSTS.RX)
   _ELINMIntMessageSize.SIZE=2;
  else
  {
   _ELINMIntMessageSize.SIZE=_ELINM_size+2;					
   _ELINM_chk=0;
   for(_ELINM_i=ELIMINT_TXMSG_INIT;_ELINM_i<_ELINMIntMessageSize.SIZE;_ELINM_i++)
    _ELINM_chk+=_ELINMIntMessageBuffer[_ELINM_i];    			// add data bytes for CRC calc.
   _ELINMIntMessageBuffer[_ELINMIntMessageSize.SIZE]=(~(_ELINM_chk+(_ELINM_chk>>8)));	// calc. Checksum
   _ELINMIntMessageSize.SIZE++;
  }
  TXSTA_SENDB=1; 							// break character bit
  TXREG=ELINMINT_DUMMY_VALUE;						// load a dummy character in TX reg. (start TX)
  _ELINMIntStatus.ELINMIntStatusByte&=0x0F;                             // reset errors
  _ELINMIntStatus.ELINMINTSTS.TX=1;      				// set TX byte
  _ELINMIntStatus1.ELINMINTSTS.HEADER=1;				// enable header timing check
  _ELINMIntStatus1.ELINMINTSTS.FRAME=1;					// enable header timing check
  _ELINMIntReadBack=0x00;						// break read back (expected)
  _ELINMIntStatus.ELINMINTSTS.IDLE=0; 					// clear the IDLE bit
 }
}

/*********************************************************************
 * Function: 	void _ELINMIntReceiveMessage(BYTE _ELINM_tag,BYTE _ELINM_id,char _ELINM_size)
 *
 * PreCondition:ELINMIntInitialize invoked
 *              RX Buffer Available (using mELINMIntRXBufferAvailable())
 *
 *
 * Input:       tag - an identifier to the message
 *              id - the ID of the message
 *              size - the size of the message
 *
 * Output:
 *
 * Side Effects:
 *
 * Stack Requirements:
 *
 * Overview:	This function requests a message to be sent by a
 *              slave. It saves the tag so the user can check which message
 *              had a problem is the case of an error being detected
 *              The ID is used to create the Header used to request
 *              the data.
 *
 *
 ********************************************************************/
void _ELINMIntReceiveMessage(BYTE _ELINM_tag,BYTE _ELINM_id,char _ELINM_size)
{
 if(_ELINMIntStatus.ELINMINTSTS.IDLE)                                   // if idle start
 {
  _ELINMIntMessageTag=_ELINM_tag;				        // save tag of the message
  _ELINMIntRXMessageSize.SIZE=_ELINM_size;			        // initiate the size of the received message
  _ELINMIntStatus.ELINMINTSTS.RX=1;                                     // set RX flag requesting Reception
  _ELINMIntRXCRC.CRC=0;                                                 // clear CRC (in fact checksum)
  mELINMIntSendMessage(_ELINMIntMessageBuffer,_ELINM_id,0);             // request the HEADER to be sent
 }
}

/*********************************************************************
 * Function:	BYTE *_ELINMIntGetPointer(char _ELINMInt_tag, BYTE _ELINMInt_position)
 *
 * PreCondition:ELINMIntInitialize invoked
 *		TX Buffer Available (using mELINMIntTXBufferAvailable())
 *
 * Input:
 *              _ELINMInt_tag -> The specific message tag  
 *              to be associated with the TX message buffer.
 *              _ELINMInt_position -> the position of the buffer to be 
 *              returned. 
 *
 * Output:	A pointer to the TX data buffer to be loaded.
 *
 * Side Effects:
 *
 * Stack Requirements:
 *
 * Overview:    This function takes a tag and associates it with a
 *              TX buffer and returns a BYTE pointer that allows the user
 *              to load the message to be transmitted. The tag lets the
 *              user identifies his(hers) message in case of an error
 *
 * OBS:         This function is not invoked directly by the user but
 *              through a macro (mELINMIntGetTXPointer(tag))
 ********************************************************************/
BYTE *_ELINMIntGetPointer(char _ELINMInt_tag, BYTE _ELINMInt_position)
{
 _ELINMIntMessageTag=_ELINMInt_tag;                                     // save the TAG
 return((BYTE *)&_ELINMIntMessageBuffer[_ELINMInt_position]);           // returns the data pointer
}
