Sample code for Arduino CAN

Dependencies:   mbed

arduinoCAN.cpp

Committer:
jedh
Date:
2016-02-29
Revision:
0:6ed8574c6258

File content as of revision 0:6ed8574c6258:

/*
 *  Copyright (C) 2009 Libelium Comunicaciones Distribuidas S.L.
 *  http://www.libelium.com
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation, either version 2.1 of the License, or
 *  (at your option) any later version.
   
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
  
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *  Version:	0.3
 *  Design:		David Gascon
 *  Implementation:		Luis Antonio Martin & Ahmad Saad
 */
 
#include "Arduino.h"
#include "arduinoCAN.h"
#include "../SPI/SPI.h"
#include "../arduino-api/arduinoMultiprotocol.h"
#include "../arduino-api/arduinoUtils.h"



#define DEBUGMODE 0
/***********************************************************************
 * Constructors
 ***********************************************************************/

CAN::CAN(void){}


/***********************************************************************
 * PUBLIC METHODS
 ***********************************************************************/

//!*************************************************************
//!	Name:	ON()													
//!	Description: 	Initialization MCP2515
//!	Param : void														
//!	Returns: "1" if no error, "0" if error							
//!*************************************************************
bool CAN::begin(uint16_t speed)
{

	#if (DEBUGMODE ==1 )
		Serial.println("-- Constructor Can(uint16_t speed) --");
	#endif
	
	// Initialize in Socket1
    Utils.setONSocket0();
    Utils.setMUXSocket0();
    Utils.unsetCSSocket0();
	
	//Initialization SPI
	SPI.begin();
	SPI.detachInterrupt();	
	SPI.setBitOrder(MSBFIRST);
	// both mode 0 & 3 should work
	SPI.setDataMode(SPI_MODE0); 
	
	//Set the SPI frequency
	SPI.setClockDivider(SPI_CLOCK_DIV128);
	
	#if (DEBUGMODE==1)
		Serial.println("SPI configured");
	#endif
	
	
	//Software resets MCP2515
	reset();
	delay(10);
	
	//After the reset enters configuration mode	
	
	//Choose the rate of CAN-bus
	switch(speed){
	  
    	case 1000:
		#if (DEBUGMODE==1)
			Serial.println("Speed=1Mbps");
		#endif
            Utils.setCSSocket0();
			SPI.transfer(SPI_WRITE);
			SPI.transfer(CNF3);
			SPI.transfer((1<<PHSEG21));	
			SPI.transfer((1<<BTLMODE)|(1<<PHSEG11));	
            Utils.unsetCSSocket0();
      	break;

    	case 500:
		#if (DEBUGMODE==1)
			Serial.println("Speed=500kps");
		#endif
            Utils.setCSSocket0();
      		SPI.transfer(SPI_WRITE);
			SPI.transfer(CNF3);
			SPI.transfer((1<<PHSEG21));	
			SPI.transfer((1<<BTLMODE)|(1<<PHSEG11));
			SPI.transfer((1<<BRP0));		
            Utils.unsetCSSocket0();
		break;

    	case 250:
		#if (DEBUGMODE==1)
			Serial.println("Speed=250kps");
		#endif
            Utils.setCSSocket0();
			SPI.transfer(SPI_WRITE);
			SPI.transfer(CNF3);
			SPI.transfer((1<<PHSEG20)|(1<<PHSEG22));	
			SPI.transfer((1<<BTLMODE)|(1<<PHSEG12)|(1<<PHSEG11)|(1<<PHSEG10));
			SPI.transfer((1<<BRP0));		
            Utils.unsetCSSocket0();
		break;

		default:
		#if (DEBUGMODE==1)
			Serial.println("The rate requested is unavailable, is set to 125 Kbit/s by default");
		#endif
            Utils.setCSSocket0();
			SPI.transfer(SPI_WRITE);
			SPI.transfer(CNF3);
			SPI.transfer((1<<PHSEG21));	
			SPI.transfer((1<<BTLMODE)|(1<<PHSEG11));
			SPI.transfer((1<<BRP2)|(1<<BRP1)|(1<<BRP0));		
            Utils.unsetCSSocket0();
		break;

  	}
	
	//Enable interrupts the Rx Buffer 
	writeRegister(CANINTE,(1<<RX1IE)|(1<<RX0IE));
		
	//Filters and masks
  	//Bufer 0: All the messages and Rollover
  	writeRegister(RXB0CTRL,(1<<RXM1)|(1<<RXM0)|(1<<BUKT)); 
	
  	//Bufer 1: All the messages
	writeRegister(RXB1CTRL,(1<<RXM1)|(1<<RXM0)); 
	
	//All bits of the mask reception delete
 	writeRegister( RXM0SIDH, 0 );
 	writeRegister( RXM0SIDL, 0 );
 	writeRegister( RXM0EID8, 0 );
  	writeRegister( RXM0EID0, 0 );
 	writeRegister( RXM1SIDH, 0 );
    writeRegister( RXM1SIDL, 0 );
  	writeRegister( RXM1EID8, 0 );
  	writeRegister( RXM1EID0, 0 );
	
	//Disable pins RXnBF pins (high impedance state) 
	writeRegister( BFPCTRL, 0 );
	
	//Set normal mode
	setMode(NORMAL_MODE);
	
	//Test its correct mode
	/*
	if (read_register(CANSTAT) != 0) {
		Serial.println("Failed to initialize the MCP2515, normal mode not activated");
		return false;
	}*/
	
	#if (DEBUGMODE==1)
		Serial.println("-- End Constructor Can(uint16_t speed) --");
		Serial.println("");
	#endif

	return 1;
}

//!*************************************************************
//!	Name:	getMessage()												
//!	Description: 	Take the CAN message								
//!	Param : messageCAN *rec_msje: pointer to reception buffer	
//!	Returns: "1" if no error, "0" if error							
//!*************************************************************
char CAN::getMessage(messageCAN *rec_msje)
{
	//Bought that buffer is the message
	char status = readStatus(SPI_RX_STATUS);
	char addr;
	static uint8_t previoSerialuffer;

	/*	
		if (bit_is_set(status,6)) {	    
			//If the message is in the Buffer 1
			addr = SPI_READ_RX;
		} 
		else if (bit_is_set(status,7)) 
		{	    
			//If the message is in the Buffer 2
			addr = SPI_READ_RX | 0x04;
		} 
		else {	    
			// Error, the message is not available
			return 0;
	  }*/

	if ( (((status & 0b11000000)>>6)&0b00000011) >2 )
	{
		addr=SPI_READ_RX | (previoSerialuffer++ & 0x01)<<2;
		#if (DEBUGMODE==1)
			Serial.println("Data in buffer available");
		#endif
	}
	else if (bit_is_set(status,6)) 
	{		
		addr = SPI_READ_RX;
		#if (DEBUGMODE==1)
			Serial.println("Data in buffer 0");
		#endif
	}
	else if (bit_is_set(status,7)) 
	{
		addr = SPI_READ_RX | 0x04;
		#if (DEBUGMODE==1)
			Serial.println("Data in buffer 1");
		#endif
	}
	else {
		// Error: no message available
		#if (DEBUGMODE==1)
			Serial.println("No messages");
		#endif

		return 0;
	}
	
    Utils.setCSSocket0();
	SPI.transfer(addr);
	
	//Read id 
	rec_msje->id  = (unsigned int) SPI.transfer(0xff) << 3;   //Read the top
	rec_msje->id |=                  SPI.transfer(0xff) >> 5;   //Read the lower
	
	//Return the Extended ID
	SPI.transfer(0xff);
	SPI.transfer(0xff);
	
	//Read DLC
	char length = SPI.transfer(0xff) & 0x0f;   
	
	rec_msje->header.length = length;
	rec_msje->header.rtr = (bit_is_set(status, 3)) ? 1 : 0;
	
	//Read data
	    for (char i=0;i<length;i++) {
			rec_msje->data[i] = SPI.transfer(0xFF);
	    }
	    
    Utils.unsetCSSocket0();
	
	//Delete the interruptions flags
	    if (bit_is_set(status, 6)) {
			bitModify(CANINTF, (1<<RX0IF), 0);
		}
		else {
			bitModify(CANINTF, (1<<RX1IF), 0);
		}
	
	return status;
}



//!*************************************************************
//!	Name:	sendMessage()												
//!	Description: 	Send the CAN message
//!	Param: messageCAN *send_msje: pointer to transmission buffer
//!	Returns: "1" if no error, "0" if error							
//!*************************************************************
char CAN::sendMessage(messageCAN *send_msje)
{
	char status = readStatus(SPI_READ_STATUS);
	
	/* Status char:
	 * 
	 * Bit	Funcion
	 *  2	TXB0CNTRL.TXREQ
	 *  4	TXB1CNTRL.TXREQ
	 *  6	TXB2CNTRL.TXREQ
	 */
	
	char address;
	
	if (bit_is_clear(status, 2)) {
		address = 0x00;
	} else if (bit_is_clear(status, 4)) {
		address = 0x02;
	} else if (bit_is_clear(status, 6)) {
		address = 0x04;
	} else {
		//All buffers are used can not send messages (returns 0)
		return 0;
	}
	
    Utils.setCSSocket0();
	SPI.transfer(SPI_WRITE_TX | address);
	//First, send the top ID10....ID3
	SPI.transfer(send_msje->id >> 3);
	//After sending the lower ID2....ID0
	SPI.transfer(send_msje->id << 5);

	//As we will not use the Extended ID you set it to 0
	SPI.transfer(0);
	SPI.transfer(0);
	
	char length = send_msje->header.length & 0x0F;
	
		if (send_msje->header.rtr) {
			SPI.transfer((1<<RTR) | length);
		} else {
		//Send the message length
			SPI.transfer(length);		
		//Send data
			for (char i=0;i<length;i++) {
				SPI.transfer(send_msje->data[i]);
			}
		}
	
    Utils.unsetCSSocket0();
	
	//Send message
    Utils.setCSSocket0();
	address = (address == 0) ? 1 : address;
	SPI.transfer(SPI_RTS | address);
    Utils.unsetCSSocket0();
	
	return address;
}


//!*************************************************************
//!	Name:	printMessage()											
//!	Description: 	CAN message print out the serial port			
//!	Param : messageCAN *msje: pointer to the message				
//!	Returns: "1" if no error, "0" if error							
//!*************************************************************
void CAN::printMessage(messageCAN *msje)
{
	Serial.print("  id: ");
	Serial.print(messageRx.id,HEX); 
	Serial.print("  rtr: ");
	Serial.print(messageRx.header.rtr,HEX); 
	Serial.print(" => ");
    
	if (!msje->header.rtr) {
		//Data
		Serial.print(" data:  ");
		Serial.print(messageRx.data[0],HEX); Serial.print(",");
		Serial.print(messageRx.data[1],HEX); Serial.print(","); 
		Serial.print(messageRx.data[2],HEX); Serial.print(",");
		Serial.print(messageRx.data[3],HEX); Serial.print(",");
		Serial.print(messageRx.data[4],HEX); Serial.print(",");
		Serial.print(messageRx.data[5],HEX); Serial.print(",");
		Serial.print(messageRx.data[6],HEX); Serial.print(",");
		Serial.println(messageRx.data[7],HEX);
	}
}


//!*************************************************************
//!	Name:	setMode()													
//!	Description: 	Configure the MCP2515							
//!	Param : uint8_t mode: The work mode							
//!	Returns: void														
//!*************************************************************
void CAN::setMode(uint8_t mode)
{
	uint8_t mode_register = 0;
	
	if (mode == LISTEN_ONLY_MODE) {
		mode_register = (0<<REQOP2)|(1<<REQOP1)|(1<<REQOP0);
	}
	else if (mode == LOOPBACK_MODE) {
		mode_register = (0<<REQOP2)|(1<<REQOP1)|(0<<REQOP0);
	}
	else if (mode == SLEEP_MODE) {
		mode_register = (0<<REQOP2)|(0<<REQOP1)|(1<<REQOP0);
	}
	else if (mode == NORMAL_MODE) {
		mode_register = (0<<REQOP2)|(0<<REQOP1)|(0<<REQOP0);
	}
		
	//Set the new mode
	bitModify(CANCTRL, (1<<REQOP2)|(1<<REQOP1)|(1<<REQOP0), mode_register);
	
	//Wait until the mode has been changed
	while ((readRegister(CANSTAT) & 0xe0) != mode_register) {	}
}


//!*************************************************************
//!	Name:	messageAvailable()										
//!	Description: 	Check if there is any message					
//!	Param : void							
//!	Returns: 1 if available, 0 if not. 														
//!*************************************************************
uint8_t CAN::messageAvailable(void)
{
	char status = readStatus(SPI_RX_STATUS);

	if ( (((status & 0b11000000)>>6)&0b00000011) >2 )
	{		
		return 1;	
	} else if (bit_is_set(status,6))  {
		return 1;
	} else if (bit_is_set(status,7)) {
		return 1;
	} else  {
		return 0;
	
		#if (DEBUGMODE==1)
			Serial.println("No data available");
		#endif		
	}
}

//**********************************************************************
// Standars PIDs
//**********************************************************************


//!*************************************************************
//!	Name:	getEngineLoad()										
//!	Description: 	Calculated engine load value				
//!	Param : void							
//!	Returns: unsigned int: engine load value (0-100)				
//!*************************************************************
unsigned int CAN::getEngineLoad()
{
	unsigned int data;

	CiARequest(CALC_ENGINE_LOAD);
	
	if (messageRx.id==ID_RESPONSE) {
		data =  uint16_t(messageRx.data[3] * 100)  / 255; 

		#if (DEBUGMODE==1)
			printMessage(&messageRx);
		#endif
	}
	
	return data;
}


//!*************************************************************
//!	Name:	getEngineCoolantTemp()								
//!	Description: 	Engine coolant temperature					
//!	Param : void							
//!	Returns: Engine coolant temperature(-40 - 215)
//!*************************************************************
unsigned int CAN::getEngineCoolantTemp()
{
	unsigned int data;

	CiARequest(ENGINE_COOLANT_TEMP);
	
	if (messageRx.id==ID_RESPONSE) {
		data =  uint16_t(messageRx.data[3] - 40); 

		#if (DEBUGMODE==1)
			printMessage(&messageRx);
		#endif
	}
	
	return data;
}


//!*************************************************************
//!	Name:	getFuelPressure()										
//!	Description: 	Fuel pressure					
//!	Param : void							
//!	Returns: unsigned int: Fuel pressure (0 - 765 Kpa)				
//!*************************************************************
unsigned int CAN::getFuelPressure()
{
	unsigned int data;

	CiARequest(FUEL_PRESSURE);
	
	if (messageRx.id==ID_RESPONSE) {
		data =  uint16_t(messageRx.data[3] * 3) ; 

		#if (DEBUGMODE==1)
			printMessage(&messageRx);
		#endif
	}
	
	return data;
}


//!*************************************************************
//!	Name:	getIntakeMAPressure()									
//!	Description: 	Intake manifold absolute pressure 				
//!	Param : void							
//!	Returns: unsigned int: absolute pressure 	(0 - 255 Kpa)		
//!*************************************************************
unsigned int CAN::getIntakeMAPressure()
{
	unsigned int data;

	CiARequest(INTAKE_M_A_PRESSURE);
	
	if (messageRx.id==ID_RESPONSE) {
		data =  messageRx.data[3]; 

		#if (DEBUGMODE==1)
			printMessage(&messageRx);
		#endif
	}
	
	return data;
	
	
}


//!*************************************************************
//!	Name:	getEngineRPM()										
//!	Description: 	Engine RPM					
//!	Param : void							
//!	Returns: unsigned int: Engine RPM (0 - 16,383 RPM) 						
//!*************************************************************
unsigned int CAN::getEngineRPM()
{	
	
	unsigned int data;

	CiARequest(ENGINE_RPM);
	
	if (messageRx.id==ID_RESPONSE) {
		data =  (uint16_t(messageRx.data[3]*256) + messageRx.data[4])/4; 

		#if (DEBUGMODE==1)
			printMessage(&messageRx);
		#endif
	}
	
	return data;
}


//!*************************************************************
//!	Name:	getVehicleSpeed()										
//!	Description: 	Vehicle speed					
//!	Param : void							
//!	Returns: unsigned int: Vehicle speed (0 - 255)				
//!*************************************************************
unsigned int CAN::getVehicleSpeed()
{
	unsigned int data;

	CiARequest(VEHICLE_SPEED);
	
	if (messageRx.id==ID_RESPONSE) {
		data =  uint16_t(messageRx.data[3]); 

		#if (DEBUGMODE==1)
			printMessage(&messageRx);
		#endif
	}
	
	return data;
	
}


//!*************************************************************
//!	Name:	getTimingAdvance()										
//!	Description: 	Timing advance					
//!	Param : void							
//!	Returns: unsigned int: Time (-64 - 63.5)		 							
//!*************************************************************
unsigned int CAN::getTimingAdvance()
{
	unsigned int data;

	CiARequest(TIMING_ADVANCE);
	
	if (messageRx.id==ID_RESPONSE) {
		data =  uint16_t(messageRx.data[3] / 2- 64); 

		#if (DEBUGMODE==1)
			printMessage(&messageRx);
		#endif
	}
	
	return data;
}
	
//!*************************************************************
//!	Name:	getIntankeAirTemp()										
//!	Description: 	Intake air temperature					
//!	Param : void							
//!	Returns: unsigned int: Intake air temperature(-40 - 215) 
//!*************************************************************
unsigned int CAN::getIntankeAirTemp()
{
	unsigned int data;

	CiARequest(INTAKE_AIR_TEMP);
	
	if (messageRx.id==ID_RESPONSE) {
		data =  uint16_t(messageRx.data[3] - 40); 

		#if (DEBUGMODE==1)
			printMessage(&messageRx);
		#endif
	}
	
	return data;
}


//!*************************************************************
//!	Name:	getMAFairFlowRate()										
//!	Description: 	MAF air flow rate					
//!	Param : void							
//!	Returns: unsigned int: air flow rate	(0 - 655.35 g/s) 						
//!*************************************************************
unsigned int CAN::getMAFairFlowRate()
{
	unsigned int data;

	CiARequest(MAF_AIR_FLOW_RATE);
	
	if (messageRx.id==ID_RESPONSE) {
		data =  ( uint16_t(messageRx.data[3] * 256) + messageRx.data[4]) / 100; 

		#if (DEBUGMODE==1)
			printMessage(&messageRx);
		#endif
	}
	
	return data;
	
}


//!*************************************************************
//!	Name:	getThrottlePosition()									
//!	Description: 	Throttle position					
//!	Param : void							
//!	Returns: unsigned int: Throttle position	(0 - 100%) 		
//!*************************************************************
unsigned int CAN::getThrottlePosition()
{
	unsigned int data;

	CiARequest(THROTTLE_POSITION);
	
	if (messageRx.id==ID_RESPONSE) {
		data =  uint16_t(messageRx.data[3] * 100) / 255; 

		#if (DEBUGMODE==1)
			printMessage(&messageRx);
		#endif
	}
	
	return data;
	
	
}


//!*************************************************************
//!	Name:	getFuelLevel()										
//!	Description: 	Fuel Level Input				
//!	Param : void							
//!	Returns: unsigned int: Fuel Level Input (0 - 100%) 							
//!*************************************************************
unsigned int CAN::getFuelLevel()
{
	unsigned int data;

	CiARequest(FUEL_LEVEL);
	
	if (messageRx.id==ID_RESPONSE) {
		data =  uint16_t(messageRx.data[3] * 100)  / 255; 

		#if (DEBUGMODE==1)
			printMessage(&messageRx);
		#endif
	}
	
	return data;
	
}


//!*************************************************************
//!	Name:	getBarometricPressure()									
//!	Description: 	Barometric pressure					
//!	Param : void							
//!	Returns: unsigned int: Barometric pressure (0 - 255 Kpa)		
//!*************************************************************
unsigned int CAN::getBarometricPressure()
{
	unsigned int data;

	CiARequest(BAROMETRIC_PRESSURE);
	
	if (messageRx.id==ID_RESPONSE) {
		data =  uint16_t(messageRx.data[3]); 

		#if (DEBUGMODE == 1)
			printMessage(&messageRx);
		#endif
	}
	
	return data;
}


//!*************************************************************
//!	Name:	getEngineFuelRate()										
//!	Description: 	Engine fuel rate					
//!	Param : void							
//!	Returns: unsigned int: Engine fuel rate	 (0 - 3212 L/h)	
//!*************************************************************
unsigned int CAN::getEngineFuelRate()
{
	unsigned int data;

	CiARequest(ENGINE_FUEL_RATE);
	
	if (messageRx.id==ID_RESPONSE) {
		data = uint16_t((messageRx.data[3] * 256) + messageRx.data[4]) * 0.05; 

		#if (DEBUGMODE==1)
			printMessage(&messageRx);
		#endif
	}
	
	return data;
	
}


/***********************************************************************
                          CAN in Automation (CiA)
 ***********************************************************************/

//Request information through OBD
void CAN::CiARequest(uint8_t PID)
{   
	messageTx.id = ID_QUERY;
	messageTx.header.rtr = 0;
	messageTx.header.length = 8;
	messageTx.data[0]= 0x02;
	messageTx.data[1]= 0x01;
	messageTx.data[2]= PID;  
   
	
	sendMessage(&messageTx);
	delay(5);

	if (messageAvailable()) {
		//Read the message buffers
		getMessage(&messageRx);
	}

}


/***********************************************************************
 * PRIVATE METHODS
 ***********************************************************************/

//Write a MCP2515 register
void CAN::writeRegister( char direction, char data )
{
	//CS low to select the MCP2515 
    Utils.setCSSocket0();

	SPI.transfer(SPI_WRITE);
	SPI.transfer(direction);
	SPI.transfer(data);
	
	//CS line again to release 
    Utils.unsetCSSocket0();
}


//**********************************************************************

//Read a MCP2515 register
char CAN::readRegister(char direction)
{
	char data;
	
	//CS low to select the MCP2515 
    Utils.setCSSocket0();
	
	SPI.transfer(SPI_READ);
	SPI.transfer(direction);	
	//Read data SPI
	data = SPI.transfer(0xff);	
	
	//CS line again to release 
    Utils.unsetCSSocket0();
	
	return data;
}


//***********************************************************************

//Modify a bit of the MCP2515 registers
void CAN::bitModify(char direction, char mask, char data)
{
	//CS low to select the MCP2515 
    Utils.setCSSocket0();
	
	SPI.transfer(SPI_BIT_MODIFY);
	SPI.transfer(direction);
	SPI.transfer(mask);
	SPI.transfer(data);
	
	//CS line again to release 
    Utils.unsetCSSocket0();
}


//***********************************************************************

//Check the status of the MCP2515 registers (SPI_RX_STATUS / SPI_READ_STATUS)
char CAN::readStatus(char type)
{
	char data;
	
	//CS low to select the MCP2515 
    Utils.setCSSocket0();
		
	SPI.transfer(type);	
	//Read data SPI 
	data = SPI.transfer(0xFF); 
	 	
	//CS line again to release 
    Utils.unsetCSSocket0();
	
	return data;
}


//***********************************************************************

//Check if the buffers are empty
bool CAN::checkFreeBuffer(void)
{
	char status = readStatus(SPI_READ_STATUS);
	
	
	if ((status & 0x54) == 0x54) 
	{	  
		//All buffers used
		return false;
	}
	
	return true;
}


//***********************************************************************

//Reset MCP2515
void CAN::reset(void)
{
	//CS low to select the MCP2515 
    Utils.setCSSocket0();

	SPI.transfer(SPI_RESET);	
	//CS line again to release 
    Utils.unsetCSSocket0();


	//Wait a bit to be stabilized after the reset MCP2515
	delay(10);
	
	#if (DEBUGMODE==1)
		Serial.println("The MCP2515 has been successfully reset, configuration mode activated");
	#endif
}