CAN Shield

MCP2515.cpp

/*
  MCP2515.cpp - Library for Microchip MCP2515 CAN Controller
  
  Author: David Harding
  
  Created: 11/08/2010
  
  For further information see:
  
  http://ww1.microchip.com/downloads/en/DeviceDoc/21801e.pdf
  http://en.wikipedia.org/wiki/CAN_bus
*/

#include "WProgram.h"
#include "SPI.h"
#include "MCP2515.h"
#include "MCP2515_defs.h"

MCP2515::MCP2515(byte CS_Pin, byte INT_Pin) {
  pinMode(CS_Pin, OUTPUT);
  digitalWrite(CS_Pin,HIGH);

  pinMode(INT_Pin,INPUT);
  digitalWrite(INT_Pin,HIGH);
  
  _CS = CS_Pin;
  _INT = INT_Pin;
}

/*
  Initialize MCP2515
  
  int CAN_Bus_Speed = transfer speed in kbps
  int Freq = MCP2515 oscillator frequency in MHz
  int SJW = Synchronization Jump Width Length bits - 1 to 4 (see data sheet)
  
  returns baud rate set
  
  Sending a bus speed of 0 kbps initiates AutoBaud and returns zero if no
  baud rate could be determined.  There must be two other active nodes on the bus!
*/
int MCP2515::Init(int CAN_Bus_Speed, byte Freq) {
  if(CAN_Bus_Speed>0) {
    if(_init(CAN_Bus_Speed, Freq, 1, false)) return CAN_Bus_Speed;
  } else {
      int i=0;
      byte interruptFlags = 0;
      for(i=5; i<1000; i=i+5) {
        if(_init(i, Freq, 1, true)) {
            // check for bus activity
            Write(CANINTF,0);
            delay(500); // need the bus to be communicating within this time frame
            if(Interrupt()) {
              // determine which interrupt flags have been set
              interruptFlags = Read(CANINTF);
              if(!(interruptFlags & MERRF)) {
                // to get here we must have received something without errors
                Mode(MODE_NORMAL);
                  return i;
              }
            }
        }
      }
  }
  return 0;
}

int MCP2515::Init(int CAN_Bus_Speed, byte Freq, byte SJW) {
  if(SJW < 1) SJW = 1;
  if(SJW > 4) SJW = 4;
  if(CAN_Bus_Speed>0) {
    if(_init(CAN_Bus_Speed, Freq, SJW, false)) return CAN_Bus_Speed;
  } else {
      int i=0;
      byte interruptFlags = 0;
      for(i=5; i<1000; i=i+5) {
        if(_init(i, Freq, SJW, true)) {
            // check for bus activity
            Write(CANINTF,0);
            delay(500); // need the bus to be communicating within this time frame
            if(Interrupt()) {
              // determine which interrupt flags have been set
              interruptFlags = Read(CANINTF);
              if(!(interruptFlags & MERRF)) {
                // to get here we must have received something without errors
                Mode(MODE_NORMAL);
                  return i;
              }
            }
        }
      }
  }
  return 0;
}

bool MCP2515::_init(int CAN_Bus_Speed, byte Freq, byte SJW, bool autoBaud) {
  
  // Reset MCP2515 which puts it in configuration mode
  Reset();
  
  // Calculate bit timing registers
  byte BRP;
  float TQ;
  byte BT;
  float tempBT;

  float NBT = 1.0 / (float)CAN_Bus_Speed * 1000.0; // Nominal Bit Time
  for(BRP=0;BRP<8;BRP++) {
    TQ = 2.0 * (float)(BRP + 1) / (float)Freq;
    tempBT = NBT / TQ;
      if(tempBT<=25) {
        BT = (int)tempBT;
        if(tempBT-BT==0) break;
      }
  }
  
  byte SPT = (0.7 * BT); // Sample point
  byte PRSEG = (SPT - 1) / 2;
  byte PHSEG1 = SPT - PRSEG - 1;
  byte PHSEG2 = BT - PHSEG1 - PRSEG - 1;

  // Programming requirements
  if(PRSEG + PHSEG1 < PHSEG2) return false;
  if(PHSEG2 <= SJW) return false;
  
  byte BTLMODE = 1;
  byte SAM = 0;
  
  // Set registers
  byte data = (((SJW-1) << 6) | BRP);
  Write(CNF1, data);
  Write(CNF2, ((BTLMODE << 7) | (SAM << 6) | ((PHSEG1-1) << 3) | (PRSEG-1)));
  Write(CNF3, (B10000000 | (PHSEG2-1)));
  Write(TXRTSCTRL,0);
  
  if(!autoBaud) {
    // Return to Normal mode
      if(!Mode(MODE_NORMAL)) return false;
  } else {
    // Set to Listen Only mode
      if(!Mode(MODE_LISTEN)) return false;
  }
  // Enable all interupts
  Write(CANINTE,255);
  
  // Test that we can read back from the MCP2515 what we wrote to it
  byte rtn = Read(CNF1);
  return (rtn==data);
}

void MCP2515::Reset() {
  digitalWrite(_CS,LOW);
  SPI.transfer(CAN_RESET);
  digitalWrite(_CS,HIGH);
}

byte MCP2515::Read(byte address) {
  digitalWrite(_CS,LOW);
  SPI.transfer(CAN_READ);
  SPI.transfer(address);
  byte data = SPI.transfer(0x00);
  digitalWrite(_CS,HIGH);
  return data;
}

void MCP2515::Read(byte address, byte data[], byte bytes) {
  // allows for sequential reading of registers starting at address - see data sheet
  byte i;
  digitalWrite(_CS,LOW);
  SPI.transfer(CAN_READ);
  SPI.transfer(address);
  for(i=0;i<bytes;i++) {
    data[i] = SPI.transfer(0x00);
  }
  digitalWrite(_CS,HIGH);
}

Frame MCP2515::ReadBuffer(byte buffer) {
 
  // Reads an entire RX buffer.
  // buffer should be either RXB0 or RXB1
  
  Frame message;
  
  digitalWrite(_CS,LOW);
  SPI.transfer(CAN_READ_BUFFER | (buffer<<1));
  byte byte1 = SPI.transfer(0x00); // RXBnSIDH
  byte byte2 = SPI.transfer(0x00); // RXBnSIDL
  byte byte3 = SPI.transfer(0x00); // RXBnEID8
  byte byte4 = SPI.transfer(0x00); // RXBnEID0
  byte byte5 = SPI.transfer(0x00); // RXBnDLC

  message.srr=(byte2 & B00010000);
  message.ide=(byte2 & B00001000);

  if(message.ide) {
    message.id = (byte1>>3);
    message.id = (message.id<<8) | ((byte1<<5) | ((byte2>>5)<<2) | (byte2 & B00000011));
    message.id = (message.id<<8) | byte3;
    message.id = (message.id<<8) | byte4;
  } else {
    message.id = ((byte1>>5)<<8) | ((byte1<<3) | (byte2>>5));
  }

  message.rtr=(byte5 & B01000000);
  message.dlc = (byte5 & B00001111);  // Number of data bytes
  for(int i=0;i<message.dlc;i++) {
    message.data[i] = SPI.transfer(0x00);
  }
  digitalWrite(_CS,HIGH);

  return message;
}

void MCP2515::Write(byte address, byte data) {
  digitalWrite(_CS,LOW);
  SPI.transfer(CAN_WRITE);
  SPI.transfer(address);
  SPI.transfer(data);
  digitalWrite(_CS,HIGH);
}

void MCP2515::Write(byte address, byte data[], byte bytes) {
  // allows for sequential writing of registers starting at address - see data sheet
  byte i;
  digitalWrite(_CS,LOW);
  SPI.transfer(CAN_WRITE);
  SPI.transfer(address);
  for(i=0;i<bytes;i++) {
    SPI.transfer(data[i]);
  }
  digitalWrite(_CS,HIGH);
}

void MCP2515::SendBuffer(byte buffers) {
  // buffers should be any combination of TXB0, TXB1, TXB2 ORed together, or TXB_ALL
  digitalWrite(_CS,LOW);
  SPI.transfer(CAN_RTS | buffers);
  digitalWrite(_CS,HIGH);
}

void MCP2515::LoadBuffer(byte buffer, Frame message) {
 
  // buffer should be one of TXB0, TXB1 or TXB2
  if(buffer==TXB0) buffer = 0;

  byte byte1=0; // TXBnSIDH
  byte byte2=0; // TXBnSIDL
  byte byte3=0; // TXBnEID8
  byte byte4=0; // TXBnEID0
  byte byte5=0; // TXBnDLC

  if(message.ide) {
    byte1 = byte((message.id<<3)>>24); // 8 MSBits of SID
      byte2 = byte((message.id<<11)>>24) & B11100000; // 3 LSBits of SID
      byte2 = byte2 | byte((message.id<<14)>>30); // 2 MSBits of EID
      byte2 = byte2 | B00001000; // EXIDE
    byte3 = byte((message.id<<16)>>24); // EID Bits 15-8
    byte4 = byte((message.id<<24)>>24); // EID Bits 7-0
  } else {
    byte1 = byte((message.id<<21)>>24); // 8 MSBits of SID
      byte2 = byte((message.id<<29)>>24) & B11100000; // 3 LSBits of SID
    byte3 = 0; // TXBnEID8
    byte4 = 0; // TXBnEID0
  }
  byte5 = message.dlc;
  if(message.rtr) {
    byte5 = byte5 | B01000000;
  }
  
  digitalWrite(_CS,LOW);
  SPI.transfer(CAN_LOAD_BUFFER | buffer);  
  SPI.transfer(byte1);
  SPI.transfer(byte2);
  SPI.transfer(byte3);
  SPI.transfer(byte4);
  SPI.transfer(byte5);
 
  for(int i=0;i<message.dlc;i++) {
    SPI.transfer(message.data[i]);
  }
  digitalWrite(_CS,HIGH);
}

byte MCP2515::Status() {
  digitalWrite(_CS,LOW);
  SPI.transfer(CAN_STATUS);
  byte data = SPI.transfer(0x00);
  digitalWrite(_CS,HIGH);
  return data;
  /*
  bit 7 - CANINTF.TX2IF
  bit 6 - TXB2CNTRL.TXREQ
  bit 5 - CANINTF.TX1IF
  bit 4 - TXB1CNTRL.TXREQ
  bit 3 - CANINTF.TX0IF
  bit 2 - TXB0CNTRL.TXREQ
  bit 1 - CANINTFL.RX1IF
  bit 0 - CANINTF.RX0IF
  */
}

byte MCP2515::RXStatus() {
  digitalWrite(_CS,LOW);
  SPI.transfer(CAN_RX_STATUS);
  byte data = SPI.transfer(0x00);
  digitalWrite(_CS,HIGH);
  return data;
  /*
  bit 7 - CANINTF.RX1IF
  bit 6 - CANINTF.RX0IF
  bit 5 - 
  bit 4 - RXBnSIDL.EIDE
  bit 3 - RXBnDLC.RTR
  bit 2 | 1 | 0 | Filter Match
  ------|---|---|-------------
      0 | 0 | 0 | RXF0
        0 | 0 | 1 | RXF1
        0 | 1 | 0 | RXF2
        0 | 1 | 1 | RXF3
        1 | 0 | 0 | RXF4
        1 | 0 | 1 | RXF5
        1 | 1 | 0 | RXF0 (rollover to RXB1)
        1 | 1 | 1 | RXF1 (rollover to RXB1)
  */
}

void MCP2515::BitModify(byte address, byte mask, byte data) {
  // see data sheet for explanation
  digitalWrite(_CS,LOW);
  SPI.transfer(CAN_BIT_MODIFY);
  SPI.transfer(address);
  SPI.transfer(mask);
  SPI.transfer(data);
  digitalWrite(_CS,HIGH);
}

bool MCP2515::Interrupt() {
  return (digitalRead(_INT)==LOW);
}

bool MCP2515::Mode(byte mode) {
  /*
  mode can be one of the following:
  MODE_CONFIG
  MODE_LISTEN
  MODE_LOOPBACK
  MODE_SLEEP
  MODE_NORMAL
  */
  BitModify(CANCTRL, B11100000, mode);
  delay(10); // allow for any transmissions to complete
  byte data = Read(CANSTAT); // check mode has been set
  return ((data & mode)==mode);
}