/*
* Tritium MCP2515 CAN Interface
* Copyright (c) 2006, Tritium Pty Ltd.  All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, 
* are permitted provided that the following conditions are met:
*  - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*	- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer 
*	  in the documentation and/or other materials provided with the distribution.
*	- Neither the name of Tritium Pty Ltd nor the names of its contributors may be used to endorse or promote products 
*	  derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
* IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
* OF SUCH DAMAGE. 
*
* Last Modified: J.Kennedy, Tritium Pty Ltd, 18 December 2006
*
* - Implements the following CAN interface functions
*	- can_init
*	- can_transmit
*	- can_receive
*
*/

// Include files
#include "Sunseeker2021.h"

#define can1_select() P3OUT &= ~CAN1_CONT_nCS
#define can1_deselect() P3OUT |= CAN1_CONT_nCS

// Public structures
can_message TX_can_message;
can_message RX_can_message;

// Public variables
unsigned char canctrl = 0;
unsigned char canctrlold = 0;
char canstat;
extern unsigned long can_msg_count;
extern unsigned long can_err_count;
extern unsigned long can_read_cnt;

  // Private variables
unsigned char buffer[16];

void can1_canctrl(unsigned char mask, unsigned char value) {
	can1_read(CANCTRL, &canctrlold, 1);
	canctrl = (canctrl & ~mask) | (value & mask);
	can1_write(CANCTRL, &canctrl, 1);
}

/**************************************************************************************************
* PUBLIC FUNCTIONS
*************************************************************************************************/

/*
* Initialises MCP2515 CAN controller
*	- Resets MCP2515 via SPI port (switches to config mode, clears errors)
*	- Changes CLKOUT to /4 rate (4 MHz)
*	- Sets up bit timing for 1 Mbit operation
*	- Sets up receive filters and masks
*		- Rx Filter 0 = Motor controller velocity
*		- Rx Filter 1 = Unused
*		- Rx Filter 2 = Driver controls packets (for remote frame requests)
*		- Rx Filter 3 = Unused
*		- Rx Filter 4 = Unused
*		- Rx Filter 5 = Unused
*		- Rx Mask 0   = Exact message must match (all 11 bits)
*		- Rx Mask 1   = Block address must match (upper 6 bits)
*	- Enables ERROR and RX interrupts on IRQ pin
*	- Switches to normal (operating) mode
*/
void can1_init()
{
  // Set up reset and clocking
  can1_reset();
  //can_mode(interface, CANCTRL, 0x03, 0x02); // CANCTRL register, modify lower 2 bits, CLK = /4
  canctrl = 0x87;

  
  // Set up bit timing & interrupts
  buffer[0] = 0x02;			    // CNF3 register: PHSEG2 = 3Tq, No wakeup, CLKOUT = CLK
  buffer[1] = 0xC9;			    // CNF2 register: set PHSEG2 in CNF3, Triple sample, PHSEG1= 2Tq, PROP = 2Tq
//  buffer[2] = 0x00;			    // CNF1 register: SJW = 1Tq, BRP = 0
  buffer[2] = 0x03;			    // CNF1 register: SJW = 1Tq, BRP = 0
  buffer[3] = 0x23;			    // CANINTE register: enable ERROR, RX0 & RX1 interrupts on IRQ pin
  buffer[4] = 0x00;			    // CANINTF register: clear all IRQ flags
  buffer[5] = 0x00;			    // EFLG register: clear all user-changable error flags
  can1_write(CNF3, &buffer[0], 6);// Write to registers
  
  // Set up external interrupt pins RX_BF and RX1BF
  buffer[0] = 0x0F;			    // BFPCTRL register: enabele RXxBF as interrupts
  buffer[1] = 0x00;			    // TXRTSCTRL register: request to send TX
  can1_write(BFPCTRL, &buffer[0], 2);// Write to registers

  // Set up receive filtering & masks
  // RXF0 - Buffer 0
  buffer[ 0] = (unsigned char)((DC_CAN_BASE + DC_SWITCH) >> 3);
  buffer[ 1] = (unsigned char)((DC_CAN_BASE + DC_SWITCH) << 5);
  buffer[ 2] = 0x00;
  buffer[ 3] = 0x00;
  // RXF1 - Buffer 0
  buffer[ 4] = 0x00;
  buffer[ 5] = 0x00;
  buffer[ 6] = 0x00;
  buffer[ 7] = 0x00;
  // RXF2 - Buffer 1
//  buffer[ 8] = (unsigned char)((DC_CAN_BASE + DC_SWITCH) >> 3);
//  buffer[ 9] = (unsigned char)((DC_CAN_BASE + DC_SWITCH) << 5);
  buffer[ 8] = (unsigned char)((MC_CAN_BASE1) >> 3);
  buffer[ 9] = (unsigned char)((MC_CAN_BASE1) << 5);
  buffer[10] = 0x00;
  buffer[11] = 0x00;
  can1_write(RXF0SIDH, &buffer[0], 12 );
  
  // RXF3 - Buffer 1
  buffer[ 0] = 0x00;
  buffer[ 1] = 0x00;
  buffer[ 2] = 0x00;
  buffer[ 3] = 0x00;
  // RXF4 - Buffer 1
  buffer[ 4] = 0x00;
  buffer[ 5] = 0x00;
  buffer[ 6] = 0x00;
  buffer[ 7] = 0x00;
  // RXF5 - Buffer 1
  buffer[ 8] = 0x00;
  buffer[ 9] = 0x00;
  buffer[10] = 0x00;
  buffer[11] = 0x00;
  can1_write(RXF3SIDH, &buffer[0], 12 );
  
  // RXM0 - Buffer 0
//  buffer[ 0] = 0xFF;       // Match entire 11 bit ID (ID is left-justified in 32-bit mask register)
  buffer[ 0] = 0xC0;     // Match 2 MSB of 11 bit ID (ID is left-justified in 32-bit mask register)
//  buffer[ 1] = 0xE0;
  buffer[ 1] = 0x00;
  buffer[ 2] = 0x00;
  buffer[ 3] = 0x00;
  // RXM1 - Buffer 1
//  buffer[ 4] = 0xFF;		// Match entire 11 bit ID (ID is left-justified in 32-bit mask register)
  buffer[ 4] = 0xC0;		//Match 2 MSB of 11 bit ID (ID is left-justified in 32-bit mask register)
//  buffer[ 5] = 0xE0;
  buffer[ 5] = 0x00;
  buffer[ 6] = 0x00;
  buffer[ 7] = 0x00;
  can1_write(RXM0SIDH, &buffer[0], 8 );
  
//  buffer[0] = 0x04;	//enable filters & rollover
//  can1_write(RXB0CTRL, &buffer[0], 1);
//  buffer[0] = 0x04;	//enable filters
//  can1_write(RXB1CTRL, &buffer[0], 1);
        
  // Switch out of config mode into normal operating mode
  //can_mode(interface, CANCTRL, 0xE0, 0x00 );			// CANCTRL register, modify upper 3 bits, mode = Normal
  can1_canctrl(0xE0, 0x00);
}

void can2_init()
{
  
}

void can1_sources(unsigned addr0, unsigned addr1) {
//	can_read(interface, CANCTRL, &buffer[0], 1);
//	buffer[0] = (buffer[0] & 0x1F) | 0x84;
//	can_write(interface, CANCTRL, &buffer[0], 1);

	can1_canctrl(0xE0, 0x80);
	
	buffer[0] = buffer[4] = 0xFF;
	buffer[1] = buffer[5] = 0xE0;
	buffer[2] = buffer[6] = 0x00;
	buffer[3] = buffer[7] = 0x00;
	can1_write(RXM0SIDH, &buffer[0], 8);

	buffer[0] = (unsigned char)(addr0 >> 3);
	buffer[1] = (unsigned char)(addr0 << 5);
	buffer[2] = buffer[3] = 0;
	buffer[4] = (unsigned char)(addr1 >> 3);
	buffer[5] = (unsigned char)(addr1 << 5);
	buffer[6] = buffer[7] = 0;
	buffer[8] = buffer[9] = 0;
	buffer[10] = buffer[11] = 0;
	can1_write(RXF0SIDH, &buffer[0], 12);
	
	buffer[0] = buffer[1] = 0;
	buffer[4] = buffer[5] = 0;
	can1_write(RXF3SIDH, &buffer[0], 12);
	
	//can_read(interface, CANCTRL, &buffer[0], 1);
	//buffer[0] = buffer[0] & 0x1F;
	//can_write(interface, CANCTRL, &buffer[0], 1);
	
	can1_canctrl(0xE0, 0x00);
}

/*
* Receives a CAN message from the MCP2515
*	- Run this routine when an IRQ is received
*	- Query the controller to identify the source of the IRQ
*		- If it was an ERROR IRQ, read & clear the Error Flag register, and return it
*		- If it was an RX IRQ, read the message and address, and return them
*		- If both, handle the error preferentially to the message
*	- Clear the appropriate IRQ flag bits
*/
void can1_receive()
{
  can_message *RXPtr_can_message;
  unsigned char flags;
  extern char ucFLAG;
     
  can_msg_count++;
//  if (can_msg_count == 0x0040)
//  {
//    can_msg_count = 0;	// Breakpoint to observe message queue
//  }
  
  RXPtr_can_message=&(*decode_queue.PutPt);
// RXPtr_can_message=&(decode_queue.msg_fifo[0]);
//  RXPtr_can_message=&decode_queue[decode_queue->PutPt];
//  RXPtr_can_message = &RX_can_message;	// Local storage
  
    // Read out the interrupt flags register
  can1_read(CANINTF, &flags, 1 );
  
  // Check for errors
  if(( flags & MCP_IRQ_ERR ) != 0x00 )
  {
    
// Read error flags and counters
    can1_read(EFLAG, &buffer[0], 1 );
    can1_read(TEC, &buffer[1], 2 );
    // Clear error flags
    can1_mode(EFLAG, buffer[0], 0x00 );	// Modify (to '0') all bits that were set
    // Return error code, a blank address field, and error registers in data field
    RXPtr_can_message->status = CAN_ERROR;
    RXPtr_can_message->address = 0x0000;
    RXPtr_can_message->data[0] = flags;		// CANINTF
    RXPtr_can_message->data[1] = buffer[0];	// EFLG
    RXPtr_can_message->data[2] = buffer[1];	// TEC
    RXPtr_can_message->data[3] = buffer[2];	// REC
    // Clear the IRQ flag
    can1_mode(CANINTF, MCP_IRQ_ERR, 0x00 );
    can_err_count++;
  }	
  
// No error, check for received messages, buffer 0
  else if(( flags & MCP_IRQ_RXB0 ) != 0x00 ){
    // Read in the info, address & message data
    can1_read(RXB0CTRL, &buffer[0], 14 );
    // Clear the IRQ flag as fast as possible
    can1_mode(CANINTF, MCP_IRQ_RXB0, 0x00 );         
    
    // Fill out return structure
    // check for Remote Frame requests and indicate the status correctly
    if(( buffer[0] & MCP_RXB0_RTR ) == 0x00 )
    {
      RXPtr_can_message->status = CAN_OK;                         // We've received a standard data packet
      fill_data(RXPtr_can_message, buffer);                      // Fill in the data
    }
    else
    {
      RXPtr_can_message->status = CAN_RTR;                         // We've received a remote frame request - data irrelevant
    }
    RXPtr_can_message->address = ((int)(buffer[1]) << 3) | ((int)(buffer[2]) >> 5);          //convert address

    //add message to queue to be decoded
    message_fifo_PUT(&decode_queue, *RXPtr_can_message); 
    ucFLAG |= 0x20;
    can_read_cnt++;
  }
  
// No error, check for received messages, buffer 1
  else if(( flags & MCP_IRQ_RXB1 ) != 0x00 )
  {
    // Read in the info, address & message data
    can1_read(RXB1CTRL, &buffer[0], 14 );
    // Clear the IRQ flag as fast as possible
    can1_mode(CANINTF, MCP_IRQ_RXB1, 0x00 );         

    // Fill out return structure
    // check for Remote Frame requests and indicate the status correctly
    if(( buffer[0] & MCP_RXB1_RTR ) == 0x00 )
    {
      RXPtr_can_message->status = CAN_OK;                         // We've received a standard data packet
      fill_data(RXPtr_can_message, buffer);                      // Fill in the data
    }
    else
    {
      RXPtr_can_message->status = CAN_RTR;                         // We've received a remote frame request - data irrelevant
    }
    RXPtr_can_message->address = ((int)(buffer[1]) << 3) | ((int)(buffer[2]) >> 5);          //convert address
    
    message_fifo_PUT(&decode_queue, *RXPtr_can_message);            //add message to queue to be decoded
    ucFLAG |= 0x20;
    can_read_cnt++;
  }
//not handled by buffers or errors
  else
  {
    RXPtr_can_message->status = CAN_ERROR;
    RXPtr_can_message->address = 0x0001;
    RXPtr_can_message->data[0] = flags;		// CANINTF
    can_err_count++;
  }
  
  //added to tritum code to account for the fact that we could have more then one intrrupt pending at a given time
  //NOTE: YOU MUST CLEAR THE MAIN_MODE STATUS BEFORE ENTERING THIS FUNCTION OR YOU WILL CLEAR THE RETURN FLAG
  if((P4IN & CAN1_CONT_nINT)==0){
  	can1_read(CANINTF, &flags, 1 );
    if(( flags & MCP_IRQ_ERR ) != 0x00 )
    {
      canstat = 1;
      can1_read(EFLAG, &buffer[0], 1 );
      can1_read(TEC, &buffer[1], 2 );
      // Clear error flags
      can1_mode(EFLAG, buffer[0], 0x00 );	// Modify (to '0') all bits that were set
      // Clear the IRQ flag
      can1_mode(CANINTF, MCP_IRQ_ERR, 0x00 );
    }
  }
}

/*
* Transmits a CAN message to the bus
*	- Busy waits while message is sent to CAN controller
*	- Uses all available transmit buffers (3 available in CAN controller) to maximise throughput
*	- Only modifies address information if it's different from what is already set up in CAN controller
*	- Assumes constant 8-byte data length value
*/
void can1_transmit()
{
  static unsigned int buf_addr[3] = {0xFFFF, 0xFFFF, 0xFFFF};
  
  // Fill data into buffer, it's used by any address
  // Allow room at the start of the buffer for the address info if needed
  buffer[ 5] = TX_can_message.data[0];
  buffer[ 6] = TX_can_message.data[1];
  buffer[ 7] = TX_can_message.data[2];
  buffer[ 8] = TX_can_message.data[3];
  buffer[ 9] = TX_can_message.data[4];
  buffer[10] = TX_can_message.data[5];
  buffer[11] = TX_can_message.data[6];
  buffer[12] = TX_can_message.data[7];
  
  // Check if the incoming address has already been configured in a mailbox
  if( TX_can_message.address == buf_addr[0] ){
    // Mailbox 0 setup matches our new message
    // Write to TX Buffer 0, start at data registers, and initiate transmission
    can1_write_tx(0x01, &buffer[5] );		
    can1_rts(0);
  }
  else if( TX_can_message.address == buf_addr[1] ){
    // Mailbox 1 setup matches our new message
    // Write to TX Buffer 1, start at data registers, and initiate transmission
    can1_write_tx(0x03, &buffer[5] );		
    can1_rts(1 );
  }
  else if( TX_can_message.address == buf_addr[2] ){
    // Mailbox 2 setup matches our new message
    // Write to TX Buffer 2, start at data registers, and initiate transmission
    can1_write_tx(0x05, &buffer[5] );		
    can1_rts(2 );
  }
  else{
    // No matches in existing mailboxes
    // No mailboxes already configured, so we'll need to load an address - set it up
    buffer[0] = (unsigned char)(TX_can_message.address >> 3);
    buffer[1] = (unsigned char)(TX_can_message.address << 5);
    buffer[2] = 0x00;						// EID8
    buffer[3] = 0x00;						// EID0
    buffer[4] = 0x08;						// DLC = 8 bytes
    
    // Check if we've got any un-setup mailboxes free and use them
    // Otherwise, find a non-busy mailbox and set it up with our new address
    if( buf_addr[0] == 0xFFFF ){			// Mailbox 0 is free
      // Write to TX Buffer 0, start at address registers, and initiate transmission
      can1_write_tx(0x00, &buffer[0] );
      can1_rts(0);
      buf_addr[0] = TX_can_message.address;
    }									
    else if( buf_addr[1] == 0xFFFF ){		// Mailbox 1 is free
      // Write to TX Buffer 1, start at address registers, and initiate transmission
      can1_write_tx(0x02, &buffer[0] );
      can1_rts(1);
      buf_addr[1] = TX_can_message.address;
    }
    else if( buf_addr[2] == 0xFFFF ){		// Mailbox 2 is free
      // Write to TX Buffer 2, start at address registers, and initiate transmission
      can1_write_tx(0x04, &buffer[0] );
      can1_rts(2);
      buf_addr[2] = TX_can_message.address;
    }
    else {					
      
      // No mailboxes free, wait until at least one is not busy
      while(( can1_read_status() & 0x54 ) == 0x54);
      // Is it mailbox 0?
      if(( can1_read_status() & 0x04 ) == 0x00) {
        // Setup mailbox 0 and send the message
        can1_write_tx(0x00, &buffer[0] );
        can1_rts(0);
        buf_addr[0] = TX_can_message.address;
      }
      // Is it mailbox 1?
      else if(( can1_read_status() & 0x10 ) == 0x00) {
        // Setup mailbox 1 and send the message
        can1_write_tx(0x02, &buffer[0] );
        can1_rts(1);
        buf_addr[1] = TX_can_message.address;
      }
      // Is it mailbox 2?
      else if(( can1_read_status() & 0x40 ) == 0x00) {
        // Setup mailbox 2 and send the message
        can1_write_tx(0x04, &buffer[0] );
        can1_rts(2);
        buf_addr[2] = TX_can_message.address;
      }
    }			
  }
}


/**************************************************************************************************
* PRIVATE FUNCTIONS
*************************************************************************************************/

//fills data of a message from the buffer
void fill_data(can_message *can, unsigned char buff[])
{
      can->data[0] = buffer[ 6];
      can->data[1] = buffer[ 7];
      can->data[2] = buffer[ 8];
      can->data[3] = buffer[ 9];
      can->data[4] = buffer[10];
      can->data[5] = buffer[11];
      can->data[6] = buffer[12];
      can->data[7] = buffer[13];
}

/*
* Resets MCP2515 CAN controller via SPI port
*	- SPI port must be already initialised
*/
void can1_reset()
{
  can1_select();
  spi1_transmit(MCP_RESET );
  can1_deselect();
}

/*
* Reads data bytes from the MCP2515
*	- Pass in starting address, pointer to array of bytes for return data, and number of bytes to read
*/
unsigned char can1_result[10] = {0,0,0,0,0,0,0,0,0,0};

void can1_read(unsigned char address, unsigned char *ptr, unsigned char bytes )
{
  unsigned char i;
  
  if(bytes > 1) {
  	can1_select();
  	spi1_transmit(MCP_READ );
  	spi1_transmit(address );
  	for( i = 0; i < bytes; i++ )
  	{
	    *ptr++ = spi1_exchange(0x00);
  	}
  	can1_deselect();
  	
  	if(address == CANCTRL) can1_result[0] = *--ptr;
  } else {
  	can1_select();
  	spi1_transmit(MCP_READ);
  	spi1_transmit(address);
  	*ptr = spi1_exchange(0);
  	can1_deselect();
  	
  	if(address == CANCTRL) can1_result[0] = *ptr;
  	if(address == 0x2C) can1_result[1] = *ptr;
  }
}

/*
* Reads data bytes from receive buffers
*	- Pass in buffer number and start position as defined in MCP2515 datasheet
*		- For starting at data, returns 8 bytes
*		- For starting at address, returns 13 bytes
*/
void can1_read_rx(unsigned char address, unsigned char *ptr )
{
  unsigned char i;
  
  address &= 0x03;			// Force upper bits of address to be zero (they're invalid)
  address <<= 1;			// Shift input bits to correct location in command byte
  address |= MCP_READ_RX;		// Construct command byte for MCP2515
  
  can1_select();
  spi1_transmit(address );
  
  if(( address & 0x02 ) == 0x00 )       // Start at address registers
  {	
    for( i = 0; i < 13; i++ )
    {
      *ptr++ = spi1_exchange(0x00);
    }
  }
  else                                  // Start at data registers
  {									
    for( i = 0; i < 8; i++ )
    {
      *ptr++ = spi1_exchange(0x00 );
    }
  }
  can1_deselect();
}

/*
* Writes data bytes to the MCP2515
*	- Pass in starting address, pointer to array of bytes, and number of bytes to write
*/
void can1_write(unsigned char address, unsigned char *ptr, unsigned char bytes )
{
  unsigned char i;
  
  can1_select();
  spi1_transmit(MCP_WRITE );
  spi1_transmit(address );
  for( i = 0; i < (bytes-1); i++ ){
    spi1_transmit(*ptr++ );
  }
  spi1_transmit(*ptr );
  can1_deselect();
}

/*
* Writes data bytes to transmit buffers
*	- Pass in buffer number and start position as defined in MCP2515 datasheet
*		- For starting at data, accepts 8 bytes
*		- For starting at address, accepts 13 bytes
*/
void can1_write_tx(unsigned char address, unsigned char *ptr )
{
  unsigned char i;
  
  address &= 0x07;				// Force upper bits of address to be zero (they're invalid)
  address |= MCP_WRITE_TX;			// Construct command byte for MCP2515
  
  can1_select();
  spi1_transmit(address );
  
  if(( address & 0x01 ) == 0x00 ){		// Start at address registers
    for( i = 0; i < 13; i++ ){
      spi1_transmit(*ptr++);
    }
  }
  else{						// Start at data registers
    for( i = 0; i < 8; i++ ){
      spi1_transmit(*ptr++);
    }
  }
  can1_deselect();
}

/*
* Request to send selected transmit buffer
*	- Pass in address of buffer to transmit: 0, 1 or 2
*/
void can1_rts(unsigned char address )
{
  unsigned char i;
  
  // Set up address bits in command byte
  i = MCP_RTS;
  if( address == 0 ) i |= 0x01;
  else if( address == 1 ) i |= 0x02;
  else if( address == 2 ) i |= 0x04;
  
  // Write command
  can1_select();
  spi1_transmit(i);
  can1_deselect();
}

/*
* Reads MCP2515 status register
*/
unsigned char can1_read_status()
{
  unsigned char status;
  
  can1_select();
  spi1_transmit(MCP_STATUS );
  status = spi1_exchange(0x00 );
  can1_deselect();
  return status;
}

/*
* Reads MCP2515 RX status (filter match) register
*/
unsigned char can1_read_filter()
{
  unsigned char status;
  
  can1_select();
  spi1_transmit(MCP_FILTER );
  status = spi1_exchange(0x00);
  can1_deselect();
  return status;
}

/*
* Modifies selected register in MCP2515
*	- Pass in register to be modified, bit mask, and bit data
*/
void can1_mode(unsigned char address, unsigned char mask, unsigned char data )
{
  can1_select();
  spi1_transmit(MCP_MODIFY);
  spi1_transmit(address);
  spi1_transmit(mask);
  spi1_transmit(data);
  can1_deselect();
}
