diff --git a/MCP2515Calc.xlsx b/MCP2515Calc.xlsx new file mode 100644 index 0000000..66d1a30 Binary files /dev/null and b/MCP2515Calc.xlsx differ diff --git a/README.md b/README.md index 9d2b7f6..16b6d91 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,41 @@ -MCP2515 Library for Arduino +MCP_CAN Library for Arduino ============== -MCP2515 library v1.1 -This library is compatible with any shield or CAN interface that uses the MCP2515 CAN protocol controller. -Baudrates 5k, 10k, 20k, 50k, 100k, 125k, 250k, 500k, & 1000k are confirmed to work using a Peak-System PCAN-USB dongle as a reference. +MCP_CAN library v1.5 +This library is compatible with any shield or board that uses the MCP2515 or MCP25625 CAN protocol controller. + +This version supports setting the ID filter mode of the protocol controller, the BAUD rate with clock speed with the begin() function. Baudrates 5k, 10k, 20k, 50k, 100k, 125k, 250k, 500k, & 1000k using 16MHz clock on the MCP2515 are confirmed to work using a Peak-System PCAN-USB dongle as a reference. Baudrates for 8MHz and 20MHz crystals are yet to be confirmed but were calculated appropriately. + +**The readMsgBuf() functions bring in the message ID. The getCanId() function is obsolete and no longer exists, don't use it.** + +The readMsgBuf(*ID, *DLC, *DATA) function will return the ID type (extended or standard) and it will bring back the remote request status bit. +If the ID AND 0x80000000 EQUALS 0x80000000, the ID is of the Extended type, otherwise it is standard. +If the ID AND 0x40000000 EQUALS 0x40000000, the message is a remote request. + +The readMsgBuf(*ID, *EXT, *DLC, *DATA) function will return the ID unaltered and doesn't inform us of a remote request. +If EXT is true, the ID is extended. + +The sendMsgBuf(ID, DLC, DATA) function can send extended or standard IDs. +To mark an ID as extended, OR the ID with 0x80000000. +To send a remote request, OR the ID with 0x40000000. + +The sendMsgBuf(ID, EXT, DLC, DATA) has not changed other than fixing return values. + +Using the setMode() function the sketch can now put the protocol controller into sleep, loop-back, or listen-only modes as well as normal operation. Right now the code defaults to loop-back mode after the begin() function runs. I have found this to increase the stability of filtering when the controller is initialized while connected to an active bus. + +User can enable and disable (default) One-Shot transmission mode from the sketch using enOneShotTX() or disOneShotTX() respectively. + +To wake up from CAN bus activity while in sleep mode enable the wake up interrupt with setSleepWakeup(1). Passing 0 will disable the wakeup interrupt (default). Installation ============== -Copy this into your "[...]/MySketches/libraries/" folder and restart the Arduino editor. +Copy this into the "[.../MySketches/]libraries/" folder and restart the Arduino editor. + +NOTE: If an older version of the library exists (e.g. CAN_BUS_Shield) be sure to remove it from the libraries folder or replace the files with those in this library to avoid conflicts. -NOTE: If you have an older version of the library (CAN_BUS_Shield) be sure to remove - it from the libraries folder or replace the files with those in this library to avoid conflicts. + +Help and Support +============== +This is primarily for non-bug related issues: Please start a *new thread* in an appropriate area at Seeedstudio forums or Arduino.cc forums and then send me (coryjfowler) a link through the PM system, my user name is the same as it is here. I will receive an email about the PM. Keep in mind, I do this in my spare time. *Happy Coding!* diff --git a/examples/CAN_loopback/CAN_loopback.ino b/examples/CAN_loopback/CAN_loopback.ino new file mode 100644 index 0000000..dfafeac --- /dev/null +++ b/examples/CAN_loopback/CAN_loopback.ino @@ -0,0 +1,88 @@ +/* CAN Loopback Example + * This example sends a message once a second and receives that message + * no CAN bus is required. This example will test the functionality + * of the protocol controller, and connections to it. + * + * Written By: Cory J. Fowler - October 5th 2016 + */ + +#include +#include + +// CAN TX Variables +unsigned long prevTX = 0; // Variable to store last execution time +const unsigned int invlTX = 1000; // One second interval constant +byte data[] = {0xAA, 0x55, 0x01, 0x10, 0xFF, 0x12, 0x34, 0x56}; // Generic CAN data to send + +// CAN RX Variables +long unsigned int rxId; +unsigned char len; +unsigned char rxBuf[8]; + +// Serial Output String Buffer +char msgString[128]; + +// CAN0 INT and CS +#define CAN0_INT 2 // Set INT to pin 2 +MCP_CAN CAN0(10); // Set CS to pin 10 + + +void setup() +{ + Serial.begin(115200); // CAN is running at 500,000BPS; 115,200BPS is SLOW, not FAST, thus 9600 is crippling. + + // Initialize MCP2515 running at 16MHz with a baudrate of 500kb/s and the masks and filters disabled. + if(CAN0.begin(MCP_ANY, CAN_500KBPS, MCP_16MHZ) == CAN_OK) + Serial.println("MCP2515 Initialized Successfully!"); + else + Serial.println("Error Initializing MCP2515..."); + + // Since we do not set NORMAL mode, we are in loopback mode by default. + //CAN0.setMode(MCP_NORMAL); + + pinMode(CAN0_INT, INPUT); // Configuring pin for /INT input + + Serial.println("MCP2515 Library Loopback Example..."); +} + +void loop() +{ + if(!digitalRead(CAN0_INT)) // If CAN0_INT pin is low, read receive buffer + { + CAN0.readMsgBuf(&rxId, &len, rxBuf); // Read data: len = data length, buf = data byte(s) + + if((rxId & 0x80000000) == 0x80000000) // Determine if ID is standard (11 bits) or extended (29 bits) + sprintf(msgString, "Extended ID: 0x%.8lX DLC: %1d Data:", (rxId & 0x1FFFFFFF), len); + else + sprintf(msgString, "Standard ID: 0x%.3lX DLC: %1d Data:", rxId, len); + + Serial.print(msgString); + + if((rxId & 0x40000000) == 0x40000000){ // Determine if message is a remote request frame. + sprintf(msgString, " REMOTE REQUEST FRAME"); + Serial.print(msgString); + } else { + for(byte i = 0; i= invlTX){ // Send this at a one second interval. + prevTX = millis(); + byte sndStat = CAN0.sendMsgBuf(0x100, 8, data); + + if(sndStat == CAN_OK) + Serial.println("Message Sent Successfully!"); + else + Serial.println("Error Sending Message..."); + + } +} + +/********************************************************************************************************* + END FILE +*********************************************************************************************************/ diff --git a/examples/CAN_receive/CAN_receive.ino b/examples/CAN_receive/CAN_receive.ino new file mode 100644 index 0000000..958c9a0 --- /dev/null +++ b/examples/CAN_receive/CAN_receive.ino @@ -0,0 +1,62 @@ +// CAN Receive Example +// + +#include +#include + +long unsigned int rxId; +unsigned char len = 0; +unsigned char rxBuf[8]; +char msgString[128]; // Array to store serial string + +#define CAN0_INT 2 // Set INT to pin 2 +MCP_CAN CAN0(10); // Set CS to pin 10 + + +void setup() +{ + Serial.begin(115200); + + // Initialize MCP2515 running at 16MHz with a baudrate of 500kb/s and the masks and filters disabled. + if(CAN0.begin(MCP_ANY, CAN_500KBPS, MCP_16MHZ) == CAN_OK) + Serial.println("MCP2515 Initialized Successfully!"); + else + Serial.println("Error Initializing MCP2515..."); + + CAN0.setMode(MCP_NORMAL); // Set operation mode to normal so the MCP2515 sends acks to received data. + + pinMode(CAN0_INT, INPUT); // Configuring pin for /INT input + + Serial.println("MCP2515 Library Receive Example..."); +} + +void loop() +{ + if(!digitalRead(CAN0_INT)) // If CAN0_INT pin is low, read receive buffer + { + CAN0.readMsgBuf(&rxId, &len, rxBuf); // Read data: len = data length, buf = data byte(s) + + if((rxId & 0x80000000) == 0x80000000) // Determine if ID is standard (11 bits) or extended (29 bits) + sprintf(msgString, "Extended ID: 0x%.8lX DLC: %1d Data:", (rxId & 0x1FFFFFFF), len); + else + sprintf(msgString, "Standard ID: 0x%.3lX DLC: %1d Data:", rxId, len); + + Serial.print(msgString); + + if((rxId & 0x40000000) == 0x40000000){ // Determine if message is a remote request frame. + sprintf(msgString, " REMOTE REQUEST FRAME"); + Serial.print(msgString); + } else { + for(byte i = 0; i +#include + +MCP_CAN CAN0(10); // Set CS to pin 10 + +void setup() +{ + Serial.begin(115200); + + // Initialize MCP2515 running at 16MHz with a baudrate of 500kb/s and the masks and filters disabled. + if(CAN0.begin(MCP_ANY, CAN_500KBPS, MCP_16MHZ) == CAN_OK) Serial.println("MCP2515 Initialized Successfully!"); + else Serial.println("Error Initializing MCP2515..."); + + CAN0.setMode(MCP_NORMAL); // Change to normal mode to allow messages to be transmitted +} + +byte data[8] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}; + +void loop() +{ + // send data: ID = 0x100, Standard CAN Frame, Data length = 8 bytes, 'data' = array of data bytes to send + byte sndStat = CAN0.sendMsgBuf(0x100, 0, 8, data); + if(sndStat == CAN_OK){ + Serial.println("Message Sent Successfully!"); + } else { + Serial.println("Error Sending Message..."); + } + delay(100); // send data per 100ms +} + +/********************************************************************************************************* + END FILE +*********************************************************************************************************/ diff --git a/examples/CAN_sleep/CAN_sleep.ino b/examples/CAN_sleep/CAN_sleep.ino new file mode 100644 index 0000000..535849c --- /dev/null +++ b/examples/CAN_sleep/CAN_sleep.ino @@ -0,0 +1,107 @@ +// CAN Sleep Example +// By Zak Kemble, based on the CAN receive example + +// If you only have 2 devices on the CAN bus (the device running this sketch and some other device sending messages), then you may find that duplicate messages are received when waking up. +// This is because when the MCP2515 wakes up it enters LISTENONLY mode where it does not send ACKs to messages, so the transmitter will retransmit the same message a few times. + +#include +#include +#include + +#define CAN0_INT 2 // Set INT to pin 2 +MCP_CAN CAN0(10); // Set CS to pin 10 + +void setup() +{ + Serial.begin(115200); + + // Initialize MCP2515 running at 16MHz with a baudrate of 500kb/s and the masks and filters disabled. + if(CAN0.begin(MCP_ANY, CAN_500KBPS, MCP_16MHZ) == CAN_OK) + Serial.println(F("MCP2515 Initialized Successfully!")); + else + Serial.println(F("Error Initializing MCP2515...")); + + CAN0.setMode(MCP_NORMAL); // Set operation mode to normal so the MCP2515 sends acks to received data. + CAN0.setSleepWakeup(1); // Enable wake up interrupt when in sleep mode + + pinMode(CAN0_INT, INPUT); // Configuring pin for /INT input + + // Enable interrupts for the CAN0_INT pin (should be pin 2 or 3 for Uno and other ATmega328P based boards) + attachInterrupt(digitalPinToInterrupt(CAN0_INT), ISR_CAN, FALLING); + + set_sleep_mode(SLEEP_MODE_PWR_DOWN); + + Serial.println(F("MCP2515 Library Sleep Example...")); +} + +void loop() +{ + if(!digitalRead(CAN0_INT)) // If CAN0_INT pin is low, read receive buffer + { + unsigned long rxId; + byte len; + byte rxBuf[8]; + + if(CAN0.readMsgBuf(&rxId, &len, rxBuf) == CAN_OK) // Read data: len = data length, buf = data byte(s) + { + char msgString[128]; // Array to store serial string + if(rxId & CAN_IS_EXTENDED) // Determine if ID is standard (11 bits) or extended (29 bits) + sprintf_P(msgString, PSTR("Extended ID: 0x%.8lX DLC: %1d Data:"), (rxId & CAN_EXTENDED_ID), len); + else + sprintf_P(msgString, PSTR("Standard ID: 0x%.3lX DLC: %1d Data:"), rxId, len); + + Serial.print(msgString); + + if(rxId & CAN_IS_REMOTE_REQUEST) // Determine if message is a remote request frame. + Serial.print(F(" REMOTE REQUEST FRAME")); + else + { + for(byte i=0;i +#include +#include +#include + +// Change these for your network! +byte mac[] = {0x00, 0x55, 0x66, 0xEE, 0xFF, 0xFF}; +IPAddress ip(10, 100, 50, 233); +IPAddress gateway(10, 100, 50, 254); +IPAddress dest(10, 100, 50, 210); + +unsigned int localPort = 8888; +unsigned int remPort = 54321; + +unsigned long rxId; +byte len = 0; +byte rxBuf[8]; +char buffer[50]; + +MCP_CAN CAN0(9); // Set CS to pin 9 + +EthernetUDP UDP; +void setup() +{ + Serial.begin(115200); +// CAN0.begin(CAN_250KBPS); // init CAN Bus with 250kb/s baudrate + CAN0.begin(MCP_ANY, CAN_250KBPS, MCP_16MHZ); // init CAN Bus with 250kb/s baudrate at 16MHz with Mask & Filters Disabled + CAN0.setMode(MCP_NORMAL); // Set operation mode to normal so the MCP2515 sends acks to received data. + pinMode(2, INPUT); // Setting pin 2, MCP2515 /INT, to input mode + Ethernet.begin(mac,ip); // Initialize Ethernet + UDP.begin(localPort); // Initialize the UDP listen port that is currently unused! + + Serial.println("CAN to Ethernet..."); +} + +void loop() +{ + if(!digitalRead(2)) // If pin 2 is low, read receive buffer + { + CAN0.readMsgBuf(&rxId, &len, rxBuf); // Read Data: rxID = Message ID, len = Data Length, buf = Data Byte(s) +// CAN0.readMsgBuf(&len, rxBuf); // Read Data: len = Data Length, buf = Data Byte(s) +// rxId = CAN0.getCanId(); // Function will be depreciated soon due to readMsgBuf now returning ID + + sprintf(buffer, "ID: %.8lX Data: %.2X %.2X %.2X %.2X %.2X %.2X %.2X %.2X\n\r", + rxId, rxBuf[0], rxBuf[1], rxBuf[2], rxBuf[3], rxBuf[4], rxBuf[5], rxBuf[6], rxBuf[7]); + + UDP.beginPacket(dest, remPort); + UDP.write(buffer); + UDP.endPacket(); + } +} + +/********************************************************************************************************* + END FILE +*********************************************************************************************************/ diff --git a/examples/CAN_to_Ethernet/udp_listen.pl b/examples/CAN_to_Ethernet/udp_listen.pl new file mode 100644 index 0000000..23627fd --- /dev/null +++ b/examples/CAN_to_Ethernet/udp_listen.pl @@ -0,0 +1,19 @@ +#!/usr/bin/perl -w +#This works on Debian Linux +#I have not tested it on a Windows system running Perl, but it should work if all dependencies are met. +#Also, FIREWALL. <--- I guarantee that will be the issue. + +use IO::Socket; + +### Create UDP Listen Socket +my $udpsocket = new IO::Socket::INET ( + LocalPort => '54321', + Proto => 'udp', + ); + die "Could not create socket: $!\n" unless $udpsocket; + +### Data Manipulation and Display +while(1) { + $udpsocket->recv(my $data,512); + print $data; +} diff --git a/examples/Dual_CAN/Dual_CAN.ino b/examples/Dual_CAN/Dual_CAN.ino new file mode 100644 index 0000000..a7a26c3 --- /dev/null +++ b/examples/Dual_CAN/Dual_CAN.ino @@ -0,0 +1,105 @@ +// Demo: Dual CAN-BUS Shields, Data Pass-through +// Written by: Cory J. Fowler +// January 31st 2014 +// This examples the ability of this library to support more than one MCP2515 based CAN interface. + + +#include +#include + +unsigned long rxId; +byte len; +byte rxBuf[8]; + +byte txBuf0[] = {0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55}; +byte txBuf1[] = {0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA}; +char msgString[128]; // Array to store serial string + +MCP_CAN CAN0(2); // CAN0 interface usins CS on digital pin 2 +MCP_CAN CAN1(3); // CAN1 interface using CS on digital pin 3 + +#define CAN0_INT 4 //define interrupt pin for CAN0 recieve buffer +#define CAN1_INT 5 //define interrupt pin for CAN1 recieve buffer + +void setup() +{ + Serial.begin(115200); + + pinMode(CAN0_INT, INPUT_PULLUP); + pinMode(CAN1_INT, INPUT_PULLUP); + + // init CAN0 bus, baudrate: 250k@16MHz + if(CAN0.begin(MCP_ANY, CAN_250KBPS, MCP_16MHZ) == CAN_OK){ + Serial.print("CAN0: Init OK!\r\n"); + CAN0.setMode(MCP_NORMAL); + } else Serial.print("CAN0: Init Fail!!!\r\n"); + + // init CAN1 bus, baudrate: 250k@16MHz + if(CAN1.begin(MCP_ANY, CAN_250KBPS, MCP_16MHZ) == CAN_OK){ + Serial.print("CAN1: Init OK!\r\n"); + CAN1.setMode(MCP_NORMAL); + } else Serial.print("CAN1: Init Fail!!!\r\n"); + + SPI.setClockDivider(SPI_CLOCK_DIV2); // Set SPI to run at 8MHz (16MHz / 2 = 8 MHz) + + CAN0.sendMsgBuf(0x1000000, 1, 8, txBuf0); + CAN1.sendMsgBuf(0x1000001, 1, 8, txBuf1); +} + +void loop(){ + if(!digitalRead(CAN0_INT)){ // If interrupt pin is low, read CAN0 receive buffer + Serial.println("CAN0 receive buffer:"); + CAN0.readMsgBuf(&rxId, &len, rxBuf); // Read data: len = data length, buf = data byte(s) + CAN1.sendMsgBuf(rxId, 1, len, rxBuf); // Immediately send message out CAN1 interface + + if((rxId & 0x80000000) == 0x80000000) // Determine if ID is standard (11 bits) or extended (29 bits) + sprintf(msgString, "Extended ID: 0x%.8lX DLC: %1d Data:", (rxId & 0x1FFFFFFF), len); + else + sprintf(msgString, "Standard ID: 0x%.3lX DLC: %1d Data:", rxId, len); + + Serial.print(msgString); + + if((rxId & 0x40000000) == 0x40000000){ // Determine if message is a remote request frame. + sprintf(msgString, " REMOTE REQUEST FRAME"); + Serial.print(msgString); + } else { + for(byte i = 0; i +#include + +long unsigned int rxId; +unsigned char len = 0; +unsigned char rxBuf[8]; + +MCP_CAN CAN0(10); // Set CS to pin 10 + +void setup() +{ + Serial.begin(115200); + if(CAN0.begin(MCP_STDEXT, CAN_500KBPS, MCP_16MHZ) == CAN_OK) Serial.print("MCP2515 Init Okay!!\r\n"); + else Serial.print("MCP2515 Init Failed!!\r\n"); + pinMode(2, INPUT); // Setting pin 2 for /INT input + + CAN0.init_Mask(0,1,0x00FFFF00); // Init first mask... + CAN0.init_Filt(0,1,0x00FFEE00); // Init first filter... + CAN0.init_Filt(1,1,0x00FFDD00); // Init second filter... + + CAN0.init_Mask(1,1,0x00FFFF00); // Init second mask... + CAN0.init_Filt(2,1,0x00FFBB00); // Init third filter... + CAN0.init_Filt(3,1,0x00FFAA00); // Init fourth filter... + CAN0.init_Filt(4,1,0x00FF8800); // Init fifth filter... + CAN0.init_Filt(5,1,0x00FF7700); // Init sixth filter... + + Serial.println("MCP2515 Library Mask & Filter Example..."); + CAN0.setMode(MCP_NORMAL); // Change to normal mode to allow messages to be transmitted +} + +void loop() +{ + if(!digitalRead(2)) // If pin 2 is low, read receive buffer + { + CAN0.readMsgBuf(&rxId, &len, rxBuf); // Read data: len = data length, buf = data byte(s) + Serial.print("ID: "); + Serial.print(rxId, HEX); + Serial.print(" Data: "); + for(int i = 0; i +#include + +#define standard 0 +// 7E0/8 = Engine ECM +// 7E1/9 = Transmission ECM +#if standard == 1 + #define LISTEN_ID 0x7EA + #define REPLY_ID 0x7E0 + #define FUNCTIONAL_ID 0x7DF +#else + #define LISTEN_ID 0x98DAF101 + #define REPLY_ID 0x98DA01F1 + #define FUNCTIONAL_ID 0x98DB33F1 +#endif + +// CAN TX Variables +unsigned long prevTx = 0; +unsigned int invlTx = 1000; +byte txData[] = {0x02,0x01,0x00,0x55,0x55,0x55,0x55,0x55}; + +// CAN RX Variables +unsigned long rxID; +byte dlc; +byte rxBuf[8]; +char msgString[128]; // Array to store serial string + +// CAN Interrupt and Chip Select Pins +#define CAN0_INT 2 /* Set INT to pin 2 (This rarely changes) */ +MCP_CAN CAN0(9); /* Set CS to pin 9 (Old shields use pin 10) */ + + +void setup(){ + + Serial.begin(115200); + while(!Serial); + + // Initialize MCP2515 running at 16MHz with a baudrate of 500kb/s and the masks and filters disabled. + if(CAN0.begin(MCP_STDEXT, CAN_500KBPS, MCP_16MHZ) == CAN_OK) + Serial.println("MCP2515 Initialized Successfully!"); + else{ + Serial.println("Error Initializing MCP2515... Permanent failure! Check your code & connections"); + while(1); + } + +// +// // Allow all Standard IDs +// CAN0.init_Mask(0,0x00000000); // Init first mask... +// CAN0.init_Filt(0,0x00000000); // Init first filter... +// CAN0.init_Filt(1,0x00000000); // Init second filter... +// // Allow all Extended IDs +// CAN0.init_Mask(1,0x80000000); // Init second mask... +// CAN0.init_Filt(2,0x80000000); // Init third filter... +// CAN0.init_Filt(3,0x80000000); // Init fourth filter... +// CAN0.init_Filt(4,0x80000000); // Init fifth filter... +// CAN0.init_Filt(5,0x80000000); // Init sixth filter... + +#if standard == 1 + // Standard ID Filters + CAN0.init_Mask(0,0x7F00000); // Init first mask... + CAN0.init_Filt(0,0x7DF0000); // Init first filter... + CAN0.init_Filt(1,0x7E10000); // Init second filter... + + CAN0.init_Mask(1,0x7F00000); // Init second mask... + CAN0.init_Filt(2,0x7DF0000); // Init third filter... + CAN0.init_Filt(3,0x7E10000); // Init fourth filter... + CAN0.init_Filt(4,0x7DF0000); // Init fifth filter... + CAN0.init_Filt(5,0x7E10000); // Init sixth filter... + +#else + // Extended ID Filters + CAN0.init_Mask(0,0x90FF0000); // Init first mask... + CAN0.init_Filt(0,0x90DA0000); // Init first filter... + CAN0.init_Filt(1,0x90DB0000); // Init second filter... + + CAN0.init_Mask(1,0x90FF0000); // Init second mask... + CAN0.init_Filt(2,0x90DA0000); // Init third filter... + CAN0.init_Filt(3,0x90DB0000); // Init fourth filter... + CAN0.init_Filt(4,0x90DA0000); // Init fifth filter... + CAN0.init_Filt(5,0x90DB0000); // Init sixth filter... +#endif + + CAN0.setMode(MCP_NORMAL); // Set operation mode to normal so the MCP2515 sends acks to received data. + + // Having problems? ====================================================== + // If you are not receiving any messages, uncomment the setMode line below + // to test the wiring between the Ardunio and the protocol controller. + // The message that this sketch sends should be instantly received. + // ======================================================================== + //CAN0.setMode(MCP_LOOPBACK); + + pinMode(CAN0_INT, INPUT); // Configuring pin for /INT input + + Serial.println("Simple CAN OBD-II PID Request"); +} + +void loop(){ + + if(!digitalRead(CAN0_INT)){ // If CAN0_INT pin is low, read receive buffer + + CAN0.readMsgBuf(&rxID, &dlc, rxBuf); // Get CAN data + + // Display received CAN data as we receive it. + if((rxID & 0x80000000) == 0x80000000) // Determine if ID is standard (11 bits) or extended (29 bits) + sprintf(msgString, "Extended ID: 0x%.8lX DLC: %1d Data:", (rxID & 0x1FFFFFFF), dlc); + else + sprintf(msgString, "Standard ID: 0x%.3lX DLC: %1d Data:", rxID, dlc); + + Serial.print(msgString); + + if((rxID & 0x40000000) == 0x40000000){ // Determine if message is a remote request frame. + sprintf(msgString, " REMOTE REQUEST FRAME"); + Serial.print(msgString); + } else { + for(byte i = 0; i= invlTx){ + prevTx = millis(); + if(CAN0.sendMsgBuf(FUNCTIONAL_ID, 8, txData) == CAN_OK){ + Serial.println("Message Sent Successfully!"); + } else { + Serial.println("Error Sending Message..."); + } + } +} diff --git a/examples/OBD_Sim/OBD_Sim.ino b/examples/OBD_Sim/OBD_Sim.ino new file mode 100644 index 0000000..c1a3cb7 --- /dev/null +++ b/examples/OBD_Sim/OBD_Sim.ino @@ -0,0 +1,1046 @@ +/* CAN OBD & UDS Simulator + * + * Currently replies to some general OBD requests + * Place holders exist for what I've been able to locate on the Internet + * Does not currently support UDS correctly, just placeholders with mode descriptions + * + * Written By: Cory J. Fowler December 20th, 2016 + * + */ + +#include +#include + + +#define PAD 0x00 + +// What CAN ID type? Standard or Extended +#define standard 0 + +// 7E0/8 = Engine ECM +// 7E1/9 = Transmission ECM + +#if standard == 1 + #define REPLY_ID 0x7E9 + #define LISTEN_ID 0x7E1 + #define FUNCTIONAL_ID 0x7DF +#else + #define REPLY_ID 0x98DAF101 + #define LISTEN_ID 0x98DA01F1 + #define FUNCTIONAL_ID 0x98DB33F1 +#endif + + +// CAN RX Variables +unsigned long rxId; +byte dlc; +byte rxBuf[8]; + +// CAN Interrupt and Chip Select +#define CAN0_INT 2 // Set CAN0 INT to pin 2 +MCP_CAN CAN0(9); // Set CAN0 CS to pin 9 + + +void setup() +{ + Serial.begin(115200); + while(!Serial); + + // Initialize MCP2515 running at 16MHz with a baudrate of 500kb/s and the masks and filters disabled. + if(CAN0.begin(MCP_STDEXT, CAN_500KBPS, MCP_20MHZ) == CAN_OK) + Serial.println("MCP2515 Initialized Successfully!"); + else + Serial.println("Error Initializing MCP2515..."); + + +#if standard == 1 + // Standard ID Filters + CAN0.init_Mask(0,0x7F00000); // Init first mask... + CAN0.init_Filt(0,0x7DF0000); // Init first filter... + CAN0.init_Filt(1,0x7E10000); // Init second filter... + + CAN0.init_Mask(1,0x7F00000); // Init second mask... + CAN0.init_Filt(2,0x7DF0000); // Init third filter... + CAN0.init_Filt(3,0x7E10000); // Init fourth filter... + CAN0.init_Filt(4,0x7DF0000); // Init fifth filter... + CAN0.init_Filt(5,0x7E10000); // Init sixth filter... + +#else + // Extended ID Filters + CAN0.init_Mask(0,0x90FFFF00); // Init first mask... + CAN0.init_Filt(0,0x90DB3300); // Init first filter... + CAN0.init_Filt(1,0x90DA0100); // Init second filter... + + CAN0.init_Mask(1,0x90FFFF00); // Init second mask... + CAN0.init_Filt(2,0x90DB3300); // Init third filter... + CAN0.init_Filt(3,0x90DA0100); // Init fourth filter... + CAN0.init_Filt(4,0x90DB3300); // Init fifth filter... + CAN0.init_Filt(5,0x90DA0100); // Init sixth filter... +#endif + + CAN0.setMode(MCP_NORMAL); // Set operation mode to normal so the MCP2515 sends acks to received data. + + pinMode(CAN0_INT, INPUT); // Configuring pin for /INT input + + Serial.println("OBD-II CAN Simulator"); +} + +void loop() +{ + if(!digitalRead(CAN0_INT)) // If CAN0_INT pin is low, read receive buffer + { + CAN0.readMsgBuf(&rxId, &dlc, rxBuf); // Get CAN data + + // First request from most adapters... + if(rxId == FUNCTIONAL_ID){ + obdReq(rxBuf); + } + } +} + + +void obdReq(byte *data){ + byte numofBytes = data[0]; + byte mode = data[1] & 0x0F; + byte pid = data[2]; + bool tx = false; + byte txData[] = {0x00,(0x40 | mode),pid,PAD,PAD,PAD,PAD,PAD}; + + + //txData[1] = 0x40 | mode; + //txData[2] = pid; + + //============================================================================= + // MODE $01 - Show current data + //============================================================================= + if(mode == 0x01){ + if(pid == 0x00){ // Supported PIDs 01-20 + txData[0] = 0x06; + + txData[3] = 0x80; + txData[4] = 0x38; + txData[5] = 0x00; + txData[6] = 0x01; + tx = true; + } + else if(pid == 0x01){ // Monitor status since DTs cleared. + bool MIL = true; + byte DTC = 5; + txData[0] = 0x06; + + txData[3] = (MIL << 7) | (DTC & 0x7F); + txData[4] = 0x07; + txData[5] = 0xFF; + txData[6] = 0x00; + tx = true; + } +// else if(pid == 0x02){ // Freeze DTC +// } + else if(pid == 0x03){ // Fuel system status + txData[0] = 0x03; + + txData[3] = 0xFA; + tx = true; + } +// else if(pid == 0x04){ // Calculated engine load +// } + else if(pid == 0x05){ // Engine coolant temperature + txData[0] = 0x03; + + txData[3] = 0xFA; + tx = true; + } +// else if(pid == 0x06){ // Short term fuel trim - Bank 1 +// } +// else if(pid == 0x07){ // Long tern fuel trim - Bank 1 +// } +// else if(pid == 0x08){ // Short term fuel trim - Bank 2 +// } +// else if(pid == 0x09){ // Long term fuel trim - Bank 2 +// } +// else if(pid == 0x0A){ // Fuel pressure (gauge) +// } + else if(pid == 0x0B){ // Intake manifold absolute pressure + txData[0] = 0x03; + + txData[3] = 0x64; + tx = true; + } + else if(pid == 0x0C){ // Engine RPM + txData[0] = 0x04; + + txData[3] = 0x9C; + txData[4] = 0x40; + tx = true; + } + else if(pid == 0x0D){ // Vehicle speed + txData[0] = 0x03; + + txData[3] = 0xFA; + tx = true; + } +// else if(pid == 0x0E){ // Timing advance +// } + else if(pid == 0x0F){ // Intake air temperature + txData[0] = 0x03; + + txData[3] = 0xFA; + tx = true; + } +// else if(pid == 0x10){ // MAF air flow rate +// } + else if(pid == 0x11){ // Throttle position + txData[0] = 0x03; + + txData[3] = 0xFA; + tx = true; + } +// else if(pid == 0x12){ // Commanded secondary air status +// } +// else if(pid == 0x13){ // Oxygen sensors present (in 2 banks) +// } +// else if(pid == 0x14){ // Oxygen Sensor 1 (Voltage & Trim) +// } +// else if(pid == 0x15){ // Oxygen Sensor 2 (Voltage & Trim) +// } +// else if(pid == 0x16){ // Oxygen Sensor 3 (Voltage & Trim) +// } +// else if(pid == 0x17){ // Oxygen Sensor 4 (Voltage & Trim) +// } +// else if(pid == 0x18){ // Oxygen Sensor 5 (Voltage & Trim) +// } +// else if(pid == 0x19){ // Oxygen Sensor 6 (Voltage & Trim) +// } +// else if(pid == 0x1A){ // Oxygen Sensor 7 (Voltage & Trim) +// } +// else if(pid == 0x1B){ // Oxygen Sensor 8 (Voltage & Trim) +// } +// else if(pid == 0x1C){ // OBD standards this vehicle conforms to +// } +// else if(pid == 0x1D){ // Oxygen sensors present (in 4 banks) +// } +// else if(pid == 0x1E){ // Auxiliary input status +// } +// else if(pid == 0x1F){ // Run time since engine start +// } + else if(pid == 0x20){ // Supported PIDs 21-40 + txData[0] = 0x06; + + txData[3] = 0x80; + txData[4] = 0x00; + txData[5] = 0x00; + txData[6] = 0x01; + tx = true; + } + else if(pid == 0x21){ // Distance traveled with MIL on + txData[0] = 0x04; + + txData[3] = 0x00; + txData[4] = 0x23; + tx = true; + } +// else if(pid == 0x22){ // Fuel rail pressure (Relative to Manifold Vacuum) +// } +// else if(pid == 0x23){ // Fuel rail gauge pressure (diesel or gasoline direct injection) +// } +// else if(pid == 0x24){ // Oxygen Sensor 1 (Fuel to Air & Voltage) +// } +// else if(pid == 0x25){ // Oxygen Sensor 2 (Fuel to Air & Voltage) +// } +// else if(pid == 0x26){ // Oxygen Sensor 3 (Fuel to Air & Voltage) +// } +// else if(pid == 0x27){ // Oxygen Sensor 4 (Fuel to Air & Voltage) +// } +// else if(pid == 0x28){ // Oxygen Sensor 5 (Fuel to Air & Voltage) +// } +// else if(pid == 0x29){ // Oxygen Sensor 6 (Fuel to Air & Voltage) +// } +// else if(pid == 0x2A){ // Oxygen Sensor 7 (Fuel to Air & Voltage) +// } +// else if(pid == 0x2B){ // Oxygen Sensor 8 (Fuel to Air & Voltage) +// } +// else if(pid == 0x2C){ // Commanded EGR +// } +// else if(pid == 0x2D){ // EGR Error +// } +// else if(pid == 0x2E){ // Commanded evaporative purge +// } +// else if(pid == 0x2F){ // Fuel tank level input +// } +// else if(pid == 0x30){ // Warm-ups since codes cleared +// } +// else if(pid == 0x31){ // Distance traveled since codes cleared +// } +// else if(pid == 0x32){ // Evap. System Vapor Pressure +// } +// else if(pid == 0x33){ // Absolute Barometric Pressure +// } +// else if(pid == 0x34){ // Oxygen Sensor 1 (Fuel to Air & Current) +// } +// else if(pid == 0x35){ // Oxygen Sensor 2 (Fuel to Air & Current) +// } +// else if(pid == 0x36){ // Oxygen Sensor 3 (Fuel to Air & Current) +// } +// else if(pid == 0x37){ // Oxygen Sensor 4 (Fuel to Air & Current) +// } +// else if(pid == 0x38){ // Oxygen Sensor 5 (Fuel to Air & Current) +// } +// else if(pid == 0x39){ // Oxygen Sensor 6 (Fuel to Air & Current) +// } +// else if(pid == 0x3A){ // Oxygen Sensor 7 (Fuel to Air & Current) +// } +// else if(pid == 0x3B){ // Oxygen Sensor 8 (Fuel to Air & Current) +// } +// else if(pid == 0x3C){ // Catalyst Temperature: Bank 1, Sensor 1 +// } +// else if(pid == 0x3D){ // Catalyst Temperature: Bank 2, Sensor 1 +// } +// else if(pid == 0x3E){ // Catalyst Temperature: Bank 1, Sensor 2 +// } +// else if(pid == 0x3F){ // Catalyst Temperature: Bank 2, Sensor 2 +// } + else if(pid == 0x40){ // Supported PIDs 41-60 + txData[0] = 0x06; + + txData[3] = 0x00; + txData[4] = 0x08; + txData[5] = 0x00; + txData[6] = 0x0D; + tx = true; + } +// else if(pid == 0x41){ // Monitor status this drive cycle +// } +// else if(pid == 0x42){ // Control module voltage +// } +// else if(pid == 0x43){ // Absolute load value +// } +// else if(pid == 0x44){ // Fuel-Air commanded equivalence ratio +// } +// else if(pid == 0x45){ // Relative throttle position +// } +// else if(pid == 0x46){ // Ambient air temperature +// } +// else if(pid == 0x47){ // Absolute throttle position B +// } +// else if(pid == 0x48){ // Absolute throttle position C +// } +// else if(pid == 0x49){ // Accelerator pedal position D +// } +// else if(pid == 0x4A){ // Accelerator pedal position E +// } +// else if(pid == 0x4B){ // Accelerator pedal position F +// } +// else if(pid == 0x4C){ // Commanded throttle actuator +// } + else if(pid == 0x4D){ // Time run with MIL on + txData[0] = 0x04; + + txData[3] = 0x00; + txData[4] = 0x3C; + tx = true; + } +// else if(pid == 0x4E){ // Time since troble codes cleared +// } +// else if(pid == 0x4F){ // Time since trouble codes cleared +// } +// else if(pid == 0x50){ // Maximum value for Fuel-Air equivalence ratio, oxygen sensor voltage, oxygen sensro current, and intake manifold absolute-pressure +// } +// else if(pid == 0x51){ // Fuel Type +// } +// else if(pid == 0x52){ // Ethanol Fuel % +// } +// else if(pid == 0x53){ // Absolute evap system vapor pressure +// } +// else if(pid == 0x54){ // Evap system vapor pressure +// } +// else if(pid == 0x55){ // Short term secondary oxygen sensor trim, A: bank 1, B: bank 3 +// } +// else if(pid == 0x56){ // Long term secondary oxygen sensor trim, A: bank 1, B: bank 3 +// } +// else if(pid == 0x57){ // Short term secondary oxygen sensor trim, A: bank 2, B: bank 4 +// } +// else if(pid == 0x58){ // Long term secondary oxygen sensor trim, A: bank 2, B: bank 4 +// } +// else if(pid == 0x59){ // Fuel rail absolute pressure +// } +// else if(pid == 0x5A){ // Relative accelerator pedal position +// } +// else if(pid == 0x5B){ // Hybrid battery pack remaining life +// } + else if(pid == 0x5C){ // Engine oil Temperature + txData[0] = 0x03; + + txData[3] = 0x1E; + tx = true; + } + else if(pid == 0x5D){ // Fuel injection timing + txData[0] = 0x04; + + txData[3] = 0x61; + txData[4] = 0x80; + tx = true; + } + else if(pid == 0x5E){ // Engine fuel rate + txData[0] = 0x04; + + txData[3] = 0x07; + txData[4] = 0xD0; + tx = true; + } +// else if(pid == 0x5F){ // Emissions requirements to which vehicle is designed +// } + else if(pid == 0x60){ // Supported PIDs 61-80 + txData[0] = 0x06; + + txData[3] = 0x00; + txData[4] = 0x00; + txData[5] = 0x00; + txData[6] = 0x01; + tx = true; + } +// else if(pid == 0x61){ // Driver's demand engine - percent torque +// } +// else if(pid == 0x62){ // Actual engine - percent torque +// } +// else if(pid == 0x63){ // Engine reference torque +// } +// else if(pid == 0x64){ // Engine percent torque data +// } +// else if(pid == 0x65){ // Auxiliary input / output supported +// } +// else if(pid == 0x66){ // Mas air flow sensor +// } +// else if(pid == 0x67){ // Engine coolant temperature +// } +// else if(pid == 0x68){ // Intake air temperature sensor +// } +// else if(pid == 0x69){ // Commanded EGR and EGR error +// } +// else if(pid == 0x6A){ // Commanded Diesel intake air flow control and relative intake air flow position +// } +// else if(pid == 0x6B){ // Exhaust gas recirculation temperature +// } +// else if(pid == 0x6C){ // Commanded throttle actuator control and relative throttle position +// } +// else if(pid == 0x6D){ // Fuel pressure control system +// } +// else if(pid == 0x6E){ // Injection pressure control system +// } +// else if(pid == 0x6F){ // Turbocharger compressor inlet pressure +// } +// else if(pid == 0x70){ // Boost pressure control +// } +// else if(pid == 0x71){ // Variable Geometry turbo sensor +// } +// else if(pid == 0x72){ // Wastegate control +// } +// else if(pid == 0x73){ // Exhaust pressure +// } +// else if(pid == 0x74){ // Turbocharger RPM +// } +// else if(pid == 0x75){ // Turbocharger temperature +// } +// else if(pid == 0x76){ // Turbocharger temperature +// } +// else if(pid == 0x77){ // Charge air cooler temperature (CACT) +// } +// else if(pid == 0x78){ // Exhaust Gas Temperature (EGT) bank 1 +// } +// else if(pid == 0x79){ // Exhaust Gas Temperature (EGT) bank 2 +// } +// else if(pid == 0x7A){ // Diesel particulate filter (DPF) +// } +// else if(pid == 0x7B){ // Diesel particulate filter (DPF) +// } +// else if(pid == 0x7C){ // Diesel particulate filter (DPF) temperature +// } +// else if(pid == 0x7D){ // NOx NTE control area status +// } +// else if(pid == 0x7E){ // PM NTE control area status +// } +// else if(pid == 0x7F){ // Engine run time +// } + else if(pid == 0x80){ // Supported PIDs 81-A0 + txData[0] = 0x06; + + txData[3] = 0x00; + txData[4] = 0x00; + txData[5] = 0x00; + txData[6] = 0x01; + tx = true; + } +// else if(pid == 0x81){ // Engine run time for Auxiliary Emissions Control Device (AECD) +// } +// else if(pid == 0x82){ // Engine run time for Auxiliary Emissions Control Device (AECD) +// } +// else if(pid == 0x83){ // NOx sensor +// } +// else if(pid == 0x84){ // Manifold surface temperature +// } +// else if(pid == 0x85){ // NOx reqgent system +// } +// else if(pid == 0x86){ // Particulate Matter (PM) sensor +// } +// else if(pid == 0x87){ // Intake manifold absolute pressure +// } + else if(pid == 0xA0){ // Supported PIDs A1-C0 + txData[0] = 0x06; + + txData[3] = 0x00; + txData[4] = 0x00; + txData[5] = 0x00; + txData[6] = 0x01; + tx = true; + } + else if(pid == 0xC0){ // Supported PIDs C1-E0 + txData[0] = 0x06; + + txData[3] = 0x00; + txData[4] = 0x00; + txData[5] = 0x00; + txData[6] = 0x01; + tx = true; + } + else if(pid == 0xE0){ // Supported PIDs E1-FF? + txData[0] = 0x06; + + txData[3] = 0x00; + txData[4] = 0x00; + txData[5] = 0x00; + txData[6] = 0x00; + tx = true; + } + else{ + unsupported(mode, pid); + } + } + + //============================================================================= + // MODE $02 - Show freeze frame data + //============================================================================= + else if(mode == 0x02){ + unsupported(mode, pid); + } + + //============================================================================= + // MODE $03 - Show stored DTCs + //============================================================================= + else if(mode == 0x03){ + byte DTCs[] = {(0x40 | mode), 0x05, 0xC0, 0xBA, 0x00, 0x11, 0x80, 0x13, 0x90, 0x45, 0xA0, 0x31}; + iso_tp(mode, pid, 12, DTCs); + } + + //============================================================================= + // MODE $04 - Clear DTCs and stored values + //============================================================================= + else if(mode == 0x04){ + // Need to cleat DTCs. We just acknowledge the command for now. + txData[0] = 0x01; + tx = true; + } + + //============================================================================= + // MODE $05 - Test Results, oxygen sensor monitoring (non CAN only) + //============================================================================= + else if(mode == 0x05){ + unsupported(mode, pid); + } + + //============================================================================= + // MODE $06 - Test Results, On-Board Monitoring (Oxygen sensor monitoring for CAN only) + //============================================================================= + else if(mode == 0x06){ + if(pid == 0x00){ // Supported PIDs 01-20 + txData[0] = 0x06; + + txData[3] = 0x00; + txData[4] = 0x00; + txData[5] = 0x00; + txData[6] = 0x00; + tx = true; + } + else{ + unsupported(mode, pid); + } + } + + //============================================================================= + // MODE $07 - Show pending DTCs (Detected during current or last driving cycle) + //============================================================================= + else if(mode == 0x07){ + byte DTCs[] = {(0x40 | mode), 0x05, 0xC0, 0xBA, 0x00, 0x11, 0x80, 0x13, 0x90, 0x45, 0xA0, 0x31}; + iso_tp(mode, pid, 12, DTCs); + } + + //============================================================================= + // MODE $08 - Control operation of on-board component/system + //============================================================================= + else if(mode == 0x08){ + unsupported(mode, pid); + } + + //============================================================================= + // MODE $09 - Request vehcile information + //============================================================================= + else if(mode == 0x09){ + if(pid == 0x00){ // Supported PIDs 01-20 + txData[0] = 0x06; + + txData[3] = 0x54; + txData[4] = 0x40; + txData[5] = 0x00; + txData[6] = 0x00; + tx = true; + } +// else if(pid == 0x01){ // VIN message count for PID 02. (Only for ISO 9141-2, ISO 14230-4 and SAE J1850.) +// } + else if(pid == 0x02){ // VIN (17 to 20 Bytes) Uses ISO-TP + byte VIN[] = {(0x40 | mode), pid, 0x01, 0x31, 0x5a, 0x56, 0x42, 0x50, 0x38, 0x41, 0x4d, 0x37, 0x44, 0x35, 0x32, 0x32, 0x30, 0x31, 0x38, 0x31}; + iso_tp(mode, pid, 20, VIN); + } +// else if(pid == 0x03){ // Calibration ID message count for PID 04. (Only for ISO 9141-2, ISO 14230-4 and SAE J1850.) +// } + else if(pid == 0x04){ // Calibration ID + byte CID[] = {(0x40 | mode), pid, 0x01, 0x41, 0x72, 0x64, 0x75, 0x69, 0x6E, 0x6F, 0x20, 0x4F, 0x42, 0x44, 0x49, 0x49, 0x73, 0x69, 0x6D, 0x51, 0x52, 0x53, 0x54}; + iso_tp(mode, pid, 23, CID); + } +// else if(pid == 0x05){ // Calibration Verification Number (CVN) message count for PID 06. (Only for ISO 9141-2, ISO 14230-4 and SAE J1850.) +// } + else if(pid == 0x06){ // CVN + byte CVN[] = {(0x40 | mode), pid, 0x02, 0x11, 0x42, 0x42, 0x42, 0x22, 0x43, 0x43, 0x43}; + iso_tp(mode, pid, 11, CVN); + } +// else if(pid == 0x07){ // In-use performance tracking message count for PID 08 and 0B. (Only for ISO 9141-2, ISO 14230-4 and SAE J1850.) +// } +// else if(pid == 0x08){ // In-use performance tracking for spark ignition vehicles. +// } +// else if(pid == 0x09){ // ECU name message count for PID 0A. +// } + else if(pid == 0x0A){ // ECM Name + byte ECMname[] = {(0x40 | mode), pid, 0x01, 0x45, 0x43, 0x4D, 0x00, 0x2D, 0x41, 0x72, 0x64, 0x75, 0x69, 0x6E, 0x6F, 0x4F, 0x42, 0x44, 0x49, 0x49, 0x73, 0x69, 0x6D}; + iso_tp(mode, pid, 23, ECMname); + } +// else if(pid == 0x0B){ // In-use performance tracking for compression ignition vehicles. +// } +// else if(pid == 0x0C){ // ESN message count for PID 0D. +// } + else if(pid == 0x0D){ // ESN + byte ESN[] = {(0x40 | mode), pid, 0x01, 0x41, 0x72, 0x64, 0x75, 0x69, 0x6E, 0x6F, 0x2D, 0x4F, 0x42, 0x44, 0x49, 0x49, 0x73, 0x69, 0x6D, 0x00}; + iso_tp(mode, pid, 20, ESN); + } + else{ + unsupported(mode, pid); + } + } + + //============================================================================= + // MODE $0A - Show permanent DTCs + //============================================================================= + else if(mode == 0x0A){ + byte DTCs[] = {(0x40 | mode), 0x05, 0xC0, 0xBA, 0x00, 0x11, 0x80, 0x13, 0x90, 0x45, 0xA0, 0x31}; + iso_tp(mode, pid, 12, DTCs); + } + + // UDS Modes: Diagonstic and Communications Management ======================================= + //============================================================================= + // MODE $10 - Diagnostic Session Control + //============================================================================= +// else if(mode == 0x10){ +// txData[0] = 0x03; +// +// txData[2] = mode; +// txData[3] = 0x00; +// tx = true; +// } + + //============================================================================= + // MODE $11 - ECU Reset + //============================================================================= +// else if(mode == 0x11){ +// txData[0] = 0x02; +// +// txData[2] = mode; +// tx = true; +// } + + //============================================================================= + // MODE $27 - Security Access + //============================================================================= +// else if(mode == 0x27){ +// txData[0] = 0x02; +// +// txData[2] = mode; +// txData[3] = 0x00; +// tx = true; +// } + + //============================================================================= + // MODE $28 - Communication Control + //============================================================================= +// else if(mode == 0x28){ +// txData[0] = 0x03; +// +// txData[2] = mode; +// txData[3] = 0x00; +// tx = true; +// } + + //============================================================================= + // MODE $3E - Tester Present + //============================================================================= +// else if(mode == 0x3E){ +// txData[0] = 0x03; +// +// txData[2] = mode; +// txData[3] = 0x00; +// tx = true; +// } + + //============================================================================= + // MODE $83 - Access Timing Parameters + //============================================================================= +// else if(mode == 0x83){ +// txData[0] = 0x03; +// +// txData[2] = mode; +// txData[3] = 0x00; +// tx = true; +// } + + //============================================================================= + // MODE $84 - Secured Data Transmission + //============================================================================= +// else if(mode == 0x84){ +// txData[0] = 0x03; +// +// txData[2] = mode; +// txData[3] = 0x00; +// tx = true; +// } +// + //============================================================================= + // MODE $85 - Control DTC Sentings + //============================================================================= +// else if(mode == 0x85){ +// txData[0] = 0x03; +// +// txData[2] = mode; +// txData[3] = 0x00; +// tx = true; +// } + + //============================================================================= + // MODE $86 - Response On Event + //============================================================================= +// else if(mode == 0x86){ +// txData[0] = 0x03; +// +// txData[2] = mode; +// txData[3] = 0x00; +// tx = true; +// } + + //============================================================================= + // MODE $87 - Link Control + //============================================================================= +// else if(mode == 0x87){ +// txData[0] = 0x03; +// +// txData[2] = mode; +// txData[3] = 0x00; +// tx = true; +// } + + // UDS Modes: Data Transmission ============================================================== + //============================================================================= + // MODE $22 - Read Data By Identifier + //============================================================================= +// else if(mode == 0x22){ +// txData[0] = 0x03; +// +// txData[2] = mode; +// txData[3] = 0x00; +// tx = true; +// } + + //============================================================================= + // MODE $23 - Read Memory By Address + //============================================================================= +// else if(mode == 0x23){ +// txData[0] = 0x02; +// +// txData[2] = mode; +// tx = true; +// } + + //============================================================================= + // MODE $24 - Read Scaling Data By Identifier + //============================================================================= +// else if(mode == 0x24){ +// txData[0] = 0x02; +// +// txData[2] = mode; +// txData[3] = 0x00; +// tx = true; +// } + + //============================================================================= + // MODE $2A - Read Data By Periodic Identifier + //============================================================================= +// else if(mode == 0x2A){ +// txData[0] = 0x03; +// +// txData[2] = mode; +// txData[3] = 0x00; +// tx = true; +// } + + //============================================================================= + // MODE $2C - Dynamically Define Data Identifier + //============================================================================= +// else if(mode == 0x2C){ +// txData[0] = 0x03; +// +// txData[2] = mode; +// txData[3] = 0x00; +// tx = true; +// } + + //============================================================================= + // MODE $2E - Write Data By Identifier + //============================================================================= +// else if(mode == 0x2E){ +// txData[0] = 0x03; +// +// txData[2] = mode; +// txData[3] = 0x00; +// tx = true; +// } + + //============================================================================= + // MODE $3D - Write Memory By Address + //============================================================================= +// else if(mode == 0x3D){ +// txData[0] = 0x03; +// +// txData[2] = mode; +// txData[3] = 0x00; +// tx = true; +// } + + // UDS Modes: Stored Data Transmission ======================================================= + //============================================================================= + // MODE $14 - Clear Diagnostic Information + //============================================================================= +// else if(mode == 0x14){ +// txData[0] = 0x03; +// +// txData[2] = mode; +// txData[3] = 0x00; +// tx = true; +// } + + //============================================================================= + // MODE $19 - Read DTC Information + //============================================================================= +// else if(mode == 0x19){ +// txData[0] = 0x02; +// +// txData[2] = mode; +// tx = true; +// } + + // UDS Modes: Input Output Control =========================================================== + //============================================================================= + // MODE $2F - Input Output Control By Identifier + //============================================================================= +// else if(mode == 0x2F){ +// txData[0] = 0x03; +// +// txData[2] = mode; +// txData[3] = 0x00; +// tx = true; +// } + + // UDS Modes: Remote Activation of Routine =================================================== + //============================================================================= + // MODE $31 - Routine Control + //============================================================================= +// else if(mode == 0x2F){ +// txData[0] = 0x03; +// +// txData[2] = mode; +// txData[3] = 0x00; +// tx = true; +// } + + // UDS Modes: Upload / Download ============================================================== + //============================================================================= + // MODE $34 - Request Download + //============================================================================= +// else if(mode == 0x34){ +// txData[0] = 0x03; +// +// txData[2] = mode; +// txData[3] = 0x00; +// tx = true; +// } + + //============================================================================= + // MODE $35 - Request Upload + //============================================================================= +// else if(mode == 0x35){ +// txData[0] = 0x02; +// +// txData[2] = mode; +// tx = true; +// } + + //============================================================================= + // MODE $36 - Transfer Data + //============================================================================= +// else if(mode == 0x36){ +// txData[0] = 0x02; +// +// txData[2] = mode; +// txData[3] = 0x00; +// tx = true; +// } + + //============================================================================= + // MODE $37 - Request Transfer Exit + //============================================================================= +// else if(mode == 0x37){ +// txData[0] = 0x03; +// +// txData[2] = mode; +// txData[3] = 0x00; +// tx = true; +// } + + //============================================================================= + // MODE $38 - Request File Transfer + //============================================================================= +// else if(mode == 0x38){ +// txData[0] = 0x03; +// +// txData[2] = mode; +// txData[3] = 0x00; +// tx = true; +// } + else { + unsupported(mode, pid); + } + + if(tx) + CAN0.sendMsgBuf(REPLY_ID, 8, txData); +} + + +// Generic debug serial output +void unsupported(byte mode, byte pid){ + negAck(mode, 0x12); + unsupportedPrint(mode, pid); +} + + +// Generic debug serial output +void negAck(byte mode, byte reason){ + byte txData[] = {0x03,0x7F,mode,reason,PAD,PAD,PAD,PAD}; + CAN0.sendMsgBuf(REPLY_ID, 8, txData); +} + + +// Generic debug serial output +void unsupportedPrint(byte mode, byte pid){ + char msgstring[64]; + sprintf(msgstring, "Mode $%02X: Unsupported PID $%02X requested!", mode, pid); + Serial.println(msgstring); +} + + +// Blocking example of ISO transport +void iso_tp(byte mode, byte pid, int len, byte *data){ + byte tpData[8]; + int offset = 0; + byte index = 0; +// byte packetcnt = ((len & 0x0FFF) - 6) / 7; +// if((((len & 0x0FFF) - 6) % 7) > 0) +// packetcnt++; + + // First frame + tpData[0] = 0x10 | ((len >> 8) & 0x0F); + tpData[1] = 0x00FF & len; + for(byte i=2; i<8; i++){ + tpData[i] = data[offset++]; + } + CAN0.sendMsgBuf(REPLY_ID, 8, tpData); + index++; // We sent a packet so increase our index. + + bool not_done = true; + unsigned long sepPrev = millis(); + byte sepInvl = 0; + byte frames = 0; + bool lockout = false; + while(not_done){ + // Need to wait for flow frame + if(!digitalRead(CAN0_INT)){ + CAN0.readMsgBuf(&rxId, &dlc, rxBuf); + + if((rxId == LISTEN_ID) && ((rxBuf[0] & 0xF0) == 0x30)){ + if((rxBuf[0] & 0x0F) == 0x00){ + // Continue + frames = rxBuf[1]; + sepInvl = rxBuf[2]; + lockout = true; + } else if((rxBuf[0] & 0x0F) == 0x01){ + // Wait + lockout = false; + delay(rxBuf[2]); + } else if((rxBuf[0] & 0x0F) == 0x03){ + // Abort + not_done = false; + return; + } + } + } + + if(((millis() - sepPrev) >= sepInvl) && lockout){ + sepPrev = millis(); + + tpData[0] = 0x20 | index++; + for(byte i=1; i<8; i++){ + if(offset != len) + tpData[i] = data[offset++]; + else + tpData[i] = 0x00; + } + + // Do consecutive frames as instructed via flow frame + CAN0.sendMsgBuf(REPLY_ID, 8, tpData); + + if(frames-- == 1) + lockout = false; + + } + + if(offset == len) + not_done = false; + else{ + char msgstring[32]; + sprintf(msgstring,"Offset: 0x%04X\tLen: 0x%04X", offset, len); + Serial.println(msgstring); + } + + + // Timeout + if((millis() - sepPrev) >= 1000) + not_done = false; + } + +} + + +/********************************************************************************************************* + END FILE +*********************************************************************************************************/ diff --git a/examples/Standard_MaskFilter/Standard_MaskFilter.ino b/examples/Standard_MaskFilter/Standard_MaskFilter.ino new file mode 100644 index 0000000..996ae49 --- /dev/null +++ b/examples/Standard_MaskFilter/Standard_MaskFilter.ino @@ -0,0 +1,94 @@ + +// MCP2515 Mask and Filter example for standard CAN message frames. +// Written by Cory J. Fowler (20140717) + +/*********************************************************************************** +If you send the following standard IDs below to an Arduino loaded with this sketch +you will find that 0x102 and 0x105 will not get in. + +ID in Hex - Two Data Bytes! - Filter/Mask in HEX + 0x100 + 0000 0000 0000 0000 = 0x01000000 + 0x101 + 0000 0000 0000 0000 = 0x01010000 + 0x102 + 0000 0000 0000 0000 = 0x01020000 This example will NOT be receiving this ID + 0x103 + 0000 0000 0000 0000 = 0x01030000 + 0x104 + 0000 0000 0000 0000 = 0x01040000 + 0x105 + 0000 0000 0000 0000 = 0x01050000 This example will NOT be receiving this ID + 0x106 + 0000 0000 0000 0000 = 0x01060000 + 0x107 + 0000 0000 0000 0000 = 0x01070000 + + This mask will check the filters against ID bit 8 and ID bits 3-0. + MASK + 0000 0000 0000 0000 = 0x010F0000 + + If there is an explicit filter match to those bits, the message will be passed to the + receive buffer and the interrupt pin will be set. + This example will NOT be exclusive to ONLY the above frame IDs, for that a mask such + as the below would be used: + MASK + 0000 0000 0000 0000 = 0x07FF0000 + + This mask will check the filters against all ID bits and the first data byte: + MASK + 1111 1111 0000 0000 = 0x07FFFF00 + If you use this mask and do not touch the filters below, you will find that your first + data byte must be 0x00 for the message to enter the receive buffer. + + At the moment, to disable a filter or mask, copy the value of a used filter or mask. + + Data bytes are ONLY checked when the MCP2515 is in 'MCP_STDEXT' mode via the begin + function, otherwise ('MCP_STD') only the ID is checked. +***********************************************************************************/ + + +#include +#include + +long unsigned int rxId; +unsigned char len = 0; +unsigned char rxBuf[8]; + +MCP_CAN CAN0(10); // Set CS to pin 10 + +void setup() +{ + Serial.begin(115200); + if(CAN0.begin(MCP_STDEXT, CAN_500KBPS, MCP_16MHZ) == CAN_OK) Serial.print("MCP2515 Init Okay!!\r\n"); + else Serial.print("MCP2515 Init Failed!!\r\n"); + pinMode(2, INPUT); // Setting pin 2 for /INT input + + + CAN0.init_Mask(0,0,0x010F0000); // Init first mask... + CAN0.init_Filt(0,0,0x01000000); // Init first filter... + CAN0.init_Filt(1,0,0x01010000); // Init second filter... + + CAN0.init_Mask(1,0,0x010F0000); // Init second mask... + CAN0.init_Filt(2,0,0x01030000); // Init third filter... + CAN0.init_Filt(3,0,0x01040000); // Init fourth filter... + CAN0.init_Filt(4,0,0x01060000); // Init fifth filter... + CAN0.init_Filt(5,0,0x01070000); // Init sixth filter... + + Serial.println("MCP2515 Library Mask & Filter Example..."); + CAN0.setMode(MCP_NORMAL); // Change to normal mode to allow messages to be transmitted +} + +void loop() +{ + if(!digitalRead(2)) // If pin 2 is low, read receive buffer + { + CAN0.readMsgBuf(&rxId, &len, rxBuf); // Read data: len = data length, buf = data byte(s) + Serial.print("ID: "); + Serial.print(rxId, HEX); + Serial.print(" Data: "); + for(int i = 0; i -#include - -long unsigned int rxId; -unsigned char len = 0; -unsigned char rxBuf[8]; - -MCP_CAN CAN0(10); // Set CS to pin 10 - - -void setup() -{ - Serial.begin(115200); - CAN0.begin(CAN_500KBPS); // init can bus : baudrate = 500k - pinMode(2, INPUT); // Setting pin 2 for /INT input - Serial.println("MCP2515 Library Receive Example..."); -} - -void loop() -{ - if(!digitalRead(2)) // If pin 2 is low, read receive buffer - { - CAN0.readMsgBuf(&len, rxBuf); // Read data: len = data length, buf = data byte(s) - rxId = CAN0.getCanId(); // Get message ID - Serial.print("ID: "); - Serial.print(rxId, HEX); - Serial.print(" Data: "); - for(int i = 0; i -#include - -MCP_CAN CAN0(10); // Set CS to pin 10 - -void setup() -{ - Serial.begin(115200); - // init can bus, baudrate: 500k - if(CAN0.begin(CAN_500KBPS) == CAN_OK) Serial.print("can init ok!!\r\n"); - else Serial.print("Can init fail!!\r\n"); -} - -unsigned char stmp[8] = {0, 1, 2, 3, 4, 5, 6, 7}; -void loop() -{ - // send data: id = 0x00, standrad flame, data len = 8, stmp: data buf - CAN0.sendMsgBuf(0x00, 0, 8, stmp); - delay(100); // send data per 100ms -} - -/********************************************************************************************************* - END FILE -*********************************************************************************************************/ diff --git a/keywords.txt b/keywords.txt index e7128e8..0e2b85b 100644 --- a/keywords.txt +++ b/keywords.txt @@ -13,20 +13,25 @@ mcp_can KEYWORD1 # Methods and Functions (KEYWORD2) ####################################### begin KEYWORD2 +setMode KEYWORD2 init_Mask KEYWORD2 init_Filt KEYWORD2 sendMsgBuf KEYWORD2 readMsgBuf KEYWORD2 checkReceive KEYWORD2 checkError KEYWORD2 -getCanId KEYWORD2 ####################################### # Constants (LITERAL1) ####################################### +MCP_8MHZ LITERAL1 +MCP_16MHZ LITERAL1 +MCP_20MHZ LITERAL1 +CAN_4K096BPS LITERAL1 CAN_5KBPS LITERAL1 CAN_10KBPS LITERAL1 CAN_20KBPS LITERAL1 +CAN_31K25BPS LITERAL1 CAN_40KBPS LITERAL1 CAN_50KBPS LITERAL1 CAN_80KBPS LITERAL1 @@ -36,6 +41,10 @@ CAN_200KBPS LITERAL1 CAN_250KBPS LITERAL1 CAN_500KBPS LITERAL1 CAN_1000KBPS LITERAL1 +MCP_ANY LITERAL1 +MCP_STD LITERAL1 +MCP_EXT LITERAL1 +MCP_STDEXT LITERAL1 CAN_OK LITERAL1 CAN_FAILINIT LITERAL1 CAN_FAILTX LITERAL1 @@ -45,3 +54,7 @@ CAN_CTRLERROR LITERAL1 CAN_GETTXBFTIMEOUT LITERAL1 CAN_SENDMSGTIMEOUT LITERAL1 CAN_FAIL LITERAL1 +MCP_NORMAL LITERAL1 +MCP_SLEEP LITERAL1 +MCP_LOOPBACK LITERAL1 +MCP_LISTENONLY LITERAL1 diff --git a/library.properties b/library.properties new file mode 100644 index 0000000..c379eb5 --- /dev/null +++ b/library.properties @@ -0,0 +1,9 @@ +name=mcp_can +version=1.5.1 +author=coryjfowler +maintainer=coryjfowler +sentence=MCP_CAN Library +paragraph=Adds support for Microchip CAN 2.0B protocol controllers (MCP2515, MCP25625, and similar) +category=Communication +url=https://github.com/coryjfowler/MCP_CAN_lib +architectures=* diff --git a/license.txt b/license.txt new file mode 100644 index 0000000..f2f81d6 --- /dev/null +++ b/license.txt @@ -0,0 +1,56 @@ +GNU LESSER GENERAL PUBLIC LICENSE +Version 3, 29 June 2007 + +Copyright © 2007 Free Software Foundation, Inc. + +Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. + +This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. + +0. Additional Definitions. +As used herein, “this License” refers to version 3 of the GNU Lesser General Public License, and the “GNU GPL” refers to version 3 of the GNU General Public License. + +“The Library” refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. + +An “Application” is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. + +A “Combined Work” is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the “Linked Version”. + +The “Minimal Corresponding Source” for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. + +The “Corresponding Application Code” for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. + +1. Exception to Section 3 of the GNU GPL. +You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. + +2. Conveying Modified Versions. +If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: + +a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or +b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. +3. Object Code Incorporating Material from Library Header Files. +The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: + +a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. +b) Accompany the object code with a copy of the GNU GPL and this license document. +4. Combined Works. +You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: + +a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. +b) Accompany the Combined Work with a copy of the GNU GPL and this license document. +c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. +d) Do one of the following: +0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. +1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. +e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) +5. Combined Libraries. +You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: + +a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. +b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. +6. Revised Versions of the GNU Lesser General Public License. +The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. + +If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. diff --git a/mcp_can.cpp b/mcp_can.cpp index 8dfe5bc..46f3a00 100644 --- a/mcp_can.cpp +++ b/mcp_can.cpp @@ -1,10 +1,11 @@ /* mcp_can.cpp 2012 Copyright (c) Seeed Technology Inc. All right reserved. + 2017 Copyright (c) Cory J. Fowler All Rights Reserved. - Author:Loovee + Author: Loovee Contributor: Cory J. Fowler - 2014-1-16 + 2017-09-25 This library 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 @@ -22,245 +23,486 @@ */ #include "mcp_can.h" -#define spi_readwrite SPI.transfer +#define spi_readwrite mcpSPI->transfer #define spi_read() spi_readwrite(0x00) /********************************************************************************************************* ** Function name: mcp2515_reset -** Descriptions: reset the device +** Descriptions: Performs a software reset *********************************************************************************************************/ void MCP_CAN::mcp2515_reset(void) { + mcpSPI->beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE0)); MCP2515_SELECT(); spi_readwrite(MCP_RESET); MCP2515_UNSELECT(); - delay(10); + mcpSPI->endTransaction(); + delay(5); // If the MCP2515 was in sleep mode when the reset command was issued then we need to wait a while for it to reset properly } /********************************************************************************************************* ** Function name: mcp2515_readRegister -** Descriptions: read register +** Descriptions: Read data register *********************************************************************************************************/ INT8U MCP_CAN::mcp2515_readRegister(const INT8U address) { INT8U ret; + mcpSPI->beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE0)); MCP2515_SELECT(); spi_readwrite(MCP_READ); spi_readwrite(address); ret = spi_read(); MCP2515_UNSELECT(); + mcpSPI->endTransaction(); return ret; } /********************************************************************************************************* ** Function name: mcp2515_readRegisterS -** Descriptions: read registerS +** Descriptions: Reads successive data registers *********************************************************************************************************/ void MCP_CAN::mcp2515_readRegisterS(const INT8U address, INT8U values[], const INT8U n) { - INT8U i; - MCP2515_SELECT(); - spi_readwrite(MCP_READ); - spi_readwrite(address); - // mcp2515 has auto-increment of address-pointer - for (i=0; ibeginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE0)); + MCP2515_SELECT(); + spi_readwrite(MCP_READ); + spi_readwrite(address); + // mcp2515 has auto-increment of address-pointer + for (i=0; iendTransaction(); } /********************************************************************************************************* ** Function name: mcp2515_setRegister -** Descriptions: set register +** Descriptions: Sets data register *********************************************************************************************************/ void MCP_CAN::mcp2515_setRegister(const INT8U address, const INT8U value) { + mcpSPI->beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE0)); MCP2515_SELECT(); spi_readwrite(MCP_WRITE); spi_readwrite(address); spi_readwrite(value); MCP2515_UNSELECT(); + mcpSPI->endTransaction(); } /********************************************************************************************************* ** Function name: mcp2515_setRegisterS -** Descriptions: set registerS +** Descriptions: Sets successive data registers *********************************************************************************************************/ void MCP_CAN::mcp2515_setRegisterS(const INT8U address, const INT8U values[], const INT8U n) { INT8U i; + mcpSPI->beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE0)); MCP2515_SELECT(); spi_readwrite(MCP_WRITE); spi_readwrite(address); for (i=0; iendTransaction(); } /********************************************************************************************************* ** Function name: mcp2515_modifyRegister -** Descriptions: set bit of one register +** Descriptions: Sets specific bits of a register *********************************************************************************************************/ void MCP_CAN::mcp2515_modifyRegister(const INT8U address, const INT8U mask, const INT8U data) { + mcpSPI->beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE0)); MCP2515_SELECT(); spi_readwrite(MCP_BITMOD); spi_readwrite(address); spi_readwrite(mask); spi_readwrite(data); MCP2515_UNSELECT(); + mcpSPI->endTransaction(); } /********************************************************************************************************* ** Function name: mcp2515_readStatus -** Descriptions: read mcp2515's Status +** Descriptions: Reads status register *********************************************************************************************************/ INT8U MCP_CAN::mcp2515_readStatus(void) { - INT8U i; - MCP2515_SELECT(); - spi_readwrite(MCP_READ_STATUS); - i = spi_read(); - MCP2515_UNSELECT(); - - return i; + INT8U i; + mcpSPI->beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE0)); + MCP2515_SELECT(); + spi_readwrite(MCP_READ_STATUS); + i = spi_read(); + MCP2515_UNSELECT(); + mcpSPI->endTransaction(); + return i; } /********************************************************************************************************* -** Function name: mcp2515_setCANCTRL_Mode -** Descriptions: set control mode +** Function name: setSleepWakeup +** Descriptions: Enable or disable the wake up interrupt (If disabled the MCP2515 will not be woken up by CAN bus activity) *********************************************************************************************************/ -INT8U MCP_CAN::mcp2515_setCANCTRL_Mode(const INT8U newmode) +void MCP_CAN::setSleepWakeup(const INT8U enable) { - INT8U i; - - mcp2515_modifyRegister(MCP_CANCTRL, MODE_MASK, newmode); + mcp2515_modifyRegister(MCP_CANINTE, MCP_WAKIF, enable ? MCP_WAKIF : 0); +} - i = mcp2515_readRegister(MCP_CANCTRL); - i &= MODE_MASK; +/********************************************************************************************************* +** Function name: setMode +** Descriptions: Sets control mode +*********************************************************************************************************/ +INT8U MCP_CAN::setMode(const INT8U opMode) +{ + mcpMode = opMode; + return mcp2515_setCANCTRL_Mode(mcpMode); +} - if ( i == newmode ) - { - return MCP2515_OK; - } +/********************************************************************************************************* +** Function name: mcp2515_setCANCTRL_Mode +** Descriptions: Set control mode +*********************************************************************************************************/ +INT8U MCP_CAN::mcp2515_setCANCTRL_Mode(const INT8U newmode) +{ + // If the chip is asleep and we want to change mode then a manual wake needs to be done + // This is done by setting the wake up interrupt flag + // This undocumented trick was found at https://github.com/mkleemann/can/blob/master/can_sleep_mcp2515.c + if((mcp2515_readRegister(MCP_CANSTAT) & MODE_MASK) == MCP_SLEEP && newmode != MCP_SLEEP) + { + // Make sure wake interrupt is enabled + byte wakeIntEnabled = (mcp2515_readRegister(MCP_CANINTE) & MCP_WAKIF); + if(!wakeIntEnabled) + mcp2515_modifyRegister(MCP_CANINTE, MCP_WAKIF, MCP_WAKIF); + + // Set wake flag (this does the actual waking up) + mcp2515_modifyRegister(MCP_CANINTF, MCP_WAKIF, MCP_WAKIF); + + // Wait for the chip to exit SLEEP and enter LISTENONLY mode. + + // If the chip is not connected to a CAN bus (or the bus has no other powered nodes) it will sometimes trigger the wake interrupt as soon + // as it's put to sleep, but it will stay in SLEEP mode instead of automatically switching to LISTENONLY mode. + // In this situation the mode needs to be manually set to LISTENONLY. + + if(mcp2515_requestNewMode(MCP_LISTENONLY) != MCP2515_OK) + return MCP2515_FAIL; + + // Turn wake interrupt back off if it was originally off + if(!wakeIntEnabled) + mcp2515_modifyRegister(MCP_CANINTE, MCP_WAKIF, 0); + } - return MCP2515_FAIL; + // Clear wake flag + mcp2515_modifyRegister(MCP_CANINTF, MCP_WAKIF, 0); + + return mcp2515_requestNewMode(newmode); +} +/********************************************************************************************************* +** Function name: mcp2515_requestNewMode +** Descriptions: Set control mode +*********************************************************************************************************/ +INT8U MCP_CAN::mcp2515_requestNewMode(const INT8U newmode) +{ + byte startTime = millis(); + + // Spam new mode request and wait for the operation to complete + while(1) + { + // Request new mode + // This is inside the loop as sometimes requesting the new mode once doesn't work (usually when attempting to sleep) + mcp2515_modifyRegister(MCP_CANCTRL, MODE_MASK, newmode); + + byte statReg = mcp2515_readRegister(MCP_CANSTAT); + if((statReg & MODE_MASK) == newmode) // We're now in the new mode + return MCP2515_OK; + else if((byte)(millis() - startTime) > 200) // Wait no more than 200ms for the operation to complete + return MCP2515_FAIL; + } } /********************************************************************************************************* ** Function name: mcp2515_configRate -** Descriptions: set boadrate +** Descriptions: Set baudrate *********************************************************************************************************/ -INT8U MCP_CAN::mcp2515_configRate(const INT8U canSpeed) +INT8U MCP_CAN::mcp2515_configRate(const INT8U canSpeed, const INT8U canClock) { INT8U set, cfg1, cfg2, cfg3; set = 1; - switch (canSpeed) + switch (canClock & MCP_CLOCK_SELECT) { - case (CAN_5KBPS): - cfg1 = MCP_16MHz_5kBPS_CFG1; - cfg2 = MCP_16MHz_5kBPS_CFG2; - cfg3 = MCP_16MHz_5kBPS_CFG3; - break; - - case (CAN_10KBPS): - cfg1 = MCP_16MHz_10kBPS_CFG1; - cfg2 = MCP_16MHz_10kBPS_CFG2; - cfg3 = MCP_16MHz_10kBPS_CFG3; - break; - - case (CAN_20KBPS): - cfg1 = MCP_16MHz_20kBPS_CFG1; - cfg2 = MCP_16MHz_20kBPS_CFG2; - cfg3 = MCP_16MHz_20kBPS_CFG3; - break; + case (MCP_8MHZ): + switch (canSpeed) + { + case (CAN_5KBPS): // 5KBPS + cfg1 = MCP_8MHz_5kBPS_CFG1; + cfg2 = MCP_8MHz_5kBPS_CFG2; + cfg3 = MCP_8MHz_5kBPS_CFG3; + break; + + case (CAN_10KBPS): // 10KBPS + cfg1 = MCP_8MHz_10kBPS_CFG1; + cfg2 = MCP_8MHz_10kBPS_CFG2; + cfg3 = MCP_8MHz_10kBPS_CFG3; + break; + + case (CAN_20KBPS): // 20KBPS + cfg1 = MCP_8MHz_20kBPS_CFG1; + cfg2 = MCP_8MHz_20kBPS_CFG2; + cfg3 = MCP_8MHz_20kBPS_CFG3; + break; + + case (CAN_31K25BPS): // 31.25KBPS + cfg1 = MCP_8MHz_31k25BPS_CFG1; + cfg2 = MCP_8MHz_31k25BPS_CFG2; + cfg3 = MCP_8MHz_31k25BPS_CFG3; + break; + + case (CAN_33K3BPS): // 33.33KBPS + cfg1 = MCP_8MHz_33k3BPS_CFG1; + cfg2 = MCP_8MHz_33k3BPS_CFG2; + cfg3 = MCP_8MHz_33k3BPS_CFG3; + break; + + case (CAN_40KBPS): // 40Kbps + cfg1 = MCP_8MHz_40kBPS_CFG1; + cfg2 = MCP_8MHz_40kBPS_CFG2; + cfg3 = MCP_8MHz_40kBPS_CFG3; + break; + + case (CAN_50KBPS): // 50Kbps + cfg1 = MCP_8MHz_50kBPS_CFG1; + cfg2 = MCP_8MHz_50kBPS_CFG2; + cfg3 = MCP_8MHz_50kBPS_CFG3; + break; + + case (CAN_80KBPS): // 80Kbps + cfg1 = MCP_8MHz_80kBPS_CFG1; + cfg2 = MCP_8MHz_80kBPS_CFG2; + cfg3 = MCP_8MHz_80kBPS_CFG3; + break; + + case (CAN_100KBPS): // 100Kbps + cfg1 = MCP_8MHz_100kBPS_CFG1; + cfg2 = MCP_8MHz_100kBPS_CFG2; + cfg3 = MCP_8MHz_100kBPS_CFG3; + break; + + case (CAN_125KBPS): // 125Kbps + cfg1 = MCP_8MHz_125kBPS_CFG1; + cfg2 = MCP_8MHz_125kBPS_CFG2; + cfg3 = MCP_8MHz_125kBPS_CFG3; + break; + + case (CAN_200KBPS): // 200Kbps + cfg1 = MCP_8MHz_200kBPS_CFG1; + cfg2 = MCP_8MHz_200kBPS_CFG2; + cfg3 = MCP_8MHz_200kBPS_CFG3; + break; + + case (CAN_250KBPS): // 250Kbps + cfg1 = MCP_8MHz_250kBPS_CFG1; + cfg2 = MCP_8MHz_250kBPS_CFG2; + cfg3 = MCP_8MHz_250kBPS_CFG3; + break; + + case (CAN_500KBPS): // 500Kbps + cfg1 = MCP_8MHz_500kBPS_CFG1; + cfg2 = MCP_8MHz_500kBPS_CFG2; + cfg3 = MCP_8MHz_500kBPS_CFG3; + break; - case (CAN_31K25BPS): - cfg1 = MCP_16MHz_31k25BPS_CFG1; - cfg2 = MCP_16MHz_31k25BPS_CFG2; - cfg3 = MCP_16MHz_31k25BPS_CFG3; - break; - - case (CAN_40KBPS): - cfg1 = MCP_16MHz_40kBPS_CFG1; - cfg2 = MCP_16MHz_40kBPS_CFG2; - cfg3 = MCP_16MHz_40kBPS_CFG3; - break; - - case (CAN_50KBPS): - cfg1 = MCP_16MHz_50kBPS_CFG1; - cfg2 = MCP_16MHz_50kBPS_CFG2; - cfg3 = MCP_16MHz_50kBPS_CFG3; - break; - - case (CAN_80KBPS): - cfg1 = MCP_16MHz_80kBPS_CFG1; - cfg2 = MCP_16MHz_80kBPS_CFG2; - cfg3 = MCP_16MHz_80kBPS_CFG3; - break; - - case (CAN_100KBPS): /* 100KBPS */ - cfg1 = MCP_16MHz_100kBPS_CFG1; - cfg2 = MCP_16MHz_100kBPS_CFG2; - cfg3 = MCP_16MHz_100kBPS_CFG3; - break; - - case (CAN_125KBPS): - cfg1 = MCP_16MHz_125kBPS_CFG1; - cfg2 = MCP_16MHz_125kBPS_CFG2; - cfg3 = MCP_16MHz_125kBPS_CFG3; - break; - - case (CAN_200KBPS): - cfg1 = MCP_16MHz_200kBPS_CFG1; - cfg2 = MCP_16MHz_200kBPS_CFG2; - cfg3 = MCP_16MHz_200kBPS_CFG3; + case (CAN_1000KBPS): // 1Mbps + cfg1 = MCP_8MHz_1000kBPS_CFG1; + cfg2 = MCP_8MHz_1000kBPS_CFG2; + cfg3 = MCP_8MHz_1000kBPS_CFG3; + break; + + default: + set = 0; + return MCP2515_FAIL; + break; + } break; - case (CAN_250KBPS): - cfg1 = MCP_16MHz_250kBPS_CFG1; - cfg2 = MCP_16MHz_250kBPS_CFG2; - cfg3 = MCP_16MHz_250kBPS_CFG3; + case (MCP_16MHZ): + switch (canSpeed) + { + case (CAN_5KBPS): // 5Kbps + cfg1 = MCP_16MHz_5kBPS_CFG1; + cfg2 = MCP_16MHz_5kBPS_CFG2; + cfg3 = MCP_16MHz_5kBPS_CFG3; + break; + + case (CAN_10KBPS): // 10Kbps + cfg1 = MCP_16MHz_10kBPS_CFG1; + cfg2 = MCP_16MHz_10kBPS_CFG2; + cfg3 = MCP_16MHz_10kBPS_CFG3; + break; + + case (CAN_20KBPS): // 20Kbps + cfg1 = MCP_16MHz_20kBPS_CFG1; + cfg2 = MCP_16MHz_20kBPS_CFG2; + cfg3 = MCP_16MHz_20kBPS_CFG3; + break; + + case (CAN_33K3BPS): // 20Kbps + cfg1 = MCP_16MHz_33k3BPS_CFG1; + cfg2 = MCP_16MHz_33k3BPS_CFG2; + cfg3 = MCP_16MHz_33k3BPS_CFG3; + break; + + case (CAN_40KBPS): // 40Kbps + cfg1 = MCP_16MHz_40kBPS_CFG1; + cfg2 = MCP_16MHz_40kBPS_CFG2; + cfg3 = MCP_16MHz_40kBPS_CFG3; + break; + + case (CAN_50KBPS): // 50Kbps + cfg2 = MCP_16MHz_50kBPS_CFG2; + cfg3 = MCP_16MHz_50kBPS_CFG3; + break; + + case (CAN_80KBPS): // 80Kbps + cfg1 = MCP_16MHz_80kBPS_CFG1; + cfg2 = MCP_16MHz_80kBPS_CFG2; + cfg3 = MCP_16MHz_80kBPS_CFG3; + break; + + case (CAN_100KBPS): // 100Kbps + cfg1 = MCP_16MHz_100kBPS_CFG1; + cfg2 = MCP_16MHz_100kBPS_CFG2; + cfg3 = MCP_16MHz_100kBPS_CFG3; + break; + + case (CAN_125KBPS): // 125Kbps + cfg1 = MCP_16MHz_125kBPS_CFG1; + cfg2 = MCP_16MHz_125kBPS_CFG2; + cfg3 = MCP_16MHz_125kBPS_CFG3; + break; + + case (CAN_200KBPS): // 200Kbps + cfg1 = MCP_16MHz_200kBPS_CFG1; + cfg2 = MCP_16MHz_200kBPS_CFG2; + cfg3 = MCP_16MHz_200kBPS_CFG3; + break; + + case (CAN_250KBPS): // 250Kbps + cfg1 = MCP_16MHz_250kBPS_CFG1; + cfg2 = MCP_16MHz_250kBPS_CFG2; + cfg3 = MCP_16MHz_250kBPS_CFG3; + break; + + case (CAN_500KBPS): // 500Kbps + cfg1 = MCP_16MHz_500kBPS_CFG1; + cfg2 = MCP_16MHz_500kBPS_CFG2; + cfg3 = MCP_16MHz_500kBPS_CFG3; + break; + + case (CAN_1000KBPS): // 1Mbps + cfg1 = MCP_16MHz_1000kBPS_CFG1; + cfg2 = MCP_16MHz_1000kBPS_CFG2; + cfg3 = MCP_16MHz_1000kBPS_CFG3; + break; + + default: + set = 0; + return MCP2515_FAIL; + break; + } break; - - case (CAN_500KBPS): - cfg1 = MCP_16MHz_500kBPS_CFG1; - cfg2 = MCP_16MHz_500kBPS_CFG2; - cfg3 = MCP_16MHz_500kBPS_CFG3; + + case (MCP_20MHZ): + switch (canSpeed) + { + case (CAN_40KBPS): // 40Kbps + cfg1 = MCP_20MHz_40kBPS_CFG1; + cfg2 = MCP_20MHz_40kBPS_CFG2; + cfg3 = MCP_20MHz_40kBPS_CFG3; + break; + + case (CAN_50KBPS): // 50Kbps + cfg1 = MCP_20MHz_50kBPS_CFG1; + cfg2 = MCP_20MHz_50kBPS_CFG2; + cfg3 = MCP_20MHz_50kBPS_CFG3; + break; + + case (CAN_80KBPS): // 80Kbps + cfg1 = MCP_20MHz_80kBPS_CFG1; + cfg2 = MCP_20MHz_80kBPS_CFG2; + cfg3 = MCP_20MHz_80kBPS_CFG3; + break; + + case (CAN_100KBPS): // 100Kbps + cfg1 = MCP_20MHz_100kBPS_CFG1; + cfg2 = MCP_20MHz_100kBPS_CFG2; + cfg3 = MCP_20MHz_100kBPS_CFG3; + break; + + case (CAN_125KBPS): // 125Kbps + cfg1 = MCP_20MHz_125kBPS_CFG1; + cfg2 = MCP_20MHz_125kBPS_CFG2; + cfg3 = MCP_20MHz_125kBPS_CFG3; + break; + + case (CAN_200KBPS): // 200Kbps + cfg1 = MCP_20MHz_200kBPS_CFG1; + cfg2 = MCP_20MHz_200kBPS_CFG2; + cfg3 = MCP_20MHz_200kBPS_CFG3; + break; + + case (CAN_250KBPS): // 250Kbps + cfg1 = MCP_20MHz_250kBPS_CFG1; + cfg2 = MCP_20MHz_250kBPS_CFG2; + cfg3 = MCP_20MHz_250kBPS_CFG3; + break; + + case (CAN_500KBPS): // 500Kbps + cfg1 = MCP_20MHz_500kBPS_CFG1; + cfg2 = MCP_20MHz_500kBPS_CFG2; + cfg3 = MCP_20MHz_500kBPS_CFG3; + break; + + case (CAN_1000KBPS): // 1Mbps + cfg1 = MCP_20MHz_1000kBPS_CFG1; + cfg2 = MCP_20MHz_1000kBPS_CFG2; + cfg3 = MCP_20MHz_1000kBPS_CFG3; + break; + + default: + set = 0; + return MCP2515_FAIL; + break; + } break; - case (CAN_1000KBPS): - cfg1 = MCP_16MHz_1000kBPS_CFG1; - cfg2 = MCP_16MHz_1000kBPS_CFG2; - cfg3 = MCP_16MHz_1000kBPS_CFG3; - break; - default: set = 0; + return MCP2515_FAIL; break; } + if (canClock & MCP_CLKOUT_ENABLE) { + cfg3 &= (~SOF_ENABLE); + } + if (set) { mcp2515_setRegister(MCP_CNF1, cfg1); mcp2515_setRegister(MCP_CNF2, cfg2); mcp2515_setRegister(MCP_CNF3, cfg3); return MCP2515_OK; } - else { - return MCP2515_FAIL; - } + + return MCP2515_FAIL; } /********************************************************************************************************* ** Function name: mcp2515_initCANBuffers -** Descriptions: init canbuffers +** Descriptions: Initialize Buffers, Masks, and Filters *********************************************************************************************************/ void MCP_CAN::mcp2515_initCANBuffers(void) { @@ -271,16 +513,16 @@ void MCP_CAN::mcp2515_initCANBuffers(void) INT32U ulMask = 0x00, ulFilt = 0x00; - mcp2515_write_id(MCP_RXM0SIDH, ext, ulMask); /*Set both masks to 0 */ - mcp2515_write_id(MCP_RXM1SIDH, ext, ulMask); /*Mask register ignores ext bit */ + mcp2515_write_mf(MCP_RXM0SIDH, ext, ulMask); /*Set both masks to 0 */ + mcp2515_write_mf(MCP_RXM1SIDH, ext, ulMask); /*Mask register ignores ext bit */ /* Set all filters to 0 */ - mcp2515_write_id(MCP_RXF0SIDH, ext, ulFilt); /* RXB0: extended */ - mcp2515_write_id(MCP_RXF1SIDH, std, ulFilt); /* RXB1: standard */ - mcp2515_write_id(MCP_RXF2SIDH, ext, ulFilt); /* RXB2: extended */ - mcp2515_write_id(MCP_RXF3SIDH, std, ulFilt); /* RXB3: standard */ - mcp2515_write_id(MCP_RXF4SIDH, ext, ulFilt); - mcp2515_write_id(MCP_RXF5SIDH, std, ulFilt); + mcp2515_write_mf(MCP_RXF0SIDH, ext, ulFilt); /* RXB0: extended */ + mcp2515_write_mf(MCP_RXF1SIDH, std, ulFilt); /* RXB1: standard */ + mcp2515_write_mf(MCP_RXF2SIDH, ext, ulFilt); /* RXB2: extended */ + mcp2515_write_mf(MCP_RXF3SIDH, std, ulFilt); /* RXB3: standard */ + mcp2515_write_mf(MCP_RXF4SIDH, ext, ulFilt); + mcp2515_write_mf(MCP_RXF5SIDH, std, ulFilt); /* Clear, deactivate the three */ /* transmit buffers */ @@ -302,37 +544,39 @@ void MCP_CAN::mcp2515_initCANBuffers(void) /********************************************************************************************************* ** Function name: mcp2515_init -** Descriptions: init the device +** Descriptions: Initialize the controller *********************************************************************************************************/ -INT8U MCP_CAN::mcp2515_init(const INT8U canSpeed) /* mcp2515init */ +INT8U MCP_CAN::mcp2515_init(const INT8U canIDMode, const INT8U canSpeed, const INT8U canClock) { INT8U res; mcp2515_reset(); + + mcpMode = MCP_LOOPBACK; res = mcp2515_setCANCTRL_Mode(MODE_CONFIG); if(res > 0) { #if DEBUG_MODE - Serial.print("Enter setting mode fall\r\n"); + Serial.println(F("Entering Configuration Mode Failure...")); #endif return res; } #if DEBUG_MODE - Serial.print("Enter setting mode success \r\n"); + Serial.println(F("Entering Configuration Mode Successful!")); #endif - /* set boadrate */ - if(mcp2515_configRate(canSpeed)) + // Set Baudrate + if(mcp2515_configRate(canSpeed, canClock)) { #if DEBUG_MODE - Serial.print("set rate fall!!\r\n"); + Serial.println(F("Setting Baudrate Failure...")); #endif return res; } #if DEBUG_MODE - Serial.print("set rate success!!\r\n"); + Serial.println(F("Setting Baudrate Successful!")); #endif if ( res == MCP2515_OK ) { @@ -343,42 +587,63 @@ INT8U MCP_CAN::mcp2515_init(const INT8U canSpeed) /* mcp25 /* interrupt mode */ mcp2515_setRegister(MCP_CANINTE, MCP_RX0IF | MCP_RX1IF); -#if (DEBUG_RXANY==1) - /* enable both receive-buffers */ - /* to receive any message */ - /* and enable rollover */ - mcp2515_modifyRegister(MCP_RXB0CTRL, - MCP_RXB_RX_MASK | MCP_RXB_BUKT_MASK, - MCP_RXB_RX_ANY | MCP_RXB_BUKT_MASK); - mcp2515_modifyRegister(MCP_RXB1CTRL, MCP_RXB_RX_MASK, - MCP_RXB_RX_ANY); -#else - /* enable both receive-buffers */ - /* to receive messages */ - /* with std. and ext. identifie */ - /* rs */ - /* and enable rollover */ - mcp2515_modifyRegister(MCP_RXB0CTRL, - MCP_RXB_RX_MASK | MCP_RXB_BUKT_MASK, - MCP_RXB_RX_STDEXT | MCP_RXB_BUKT_MASK ); - mcp2515_modifyRegister(MCP_RXB1CTRL, MCP_RXB_RX_MASK, - MCP_RXB_RX_STDEXT); -#endif - /* enter normal mode */ - res = mcp2515_setCANCTRL_Mode(MODE_NORMAL); + //Sets BF pins as GPO + mcp2515_setRegister(MCP_BFPCTRL,MCP_BxBFS_MASK | MCP_BxBFE_MASK); + //Sets RTS pins as GPI + mcp2515_setRegister(MCP_TXRTSCTRL,0x00); + + switch(canIDMode) + { + case (MCP_ANY): + mcp2515_modifyRegister(MCP_RXB0CTRL, + MCP_RXB_RX_MASK | MCP_RXB_BUKT_MASK, + MCP_RXB_RX_ANY | MCP_RXB_BUKT_MASK); + mcp2515_modifyRegister(MCP_RXB1CTRL, MCP_RXB_RX_MASK, + MCP_RXB_RX_ANY); + break; +/* The followingn two functions of the MCP2515 do not work, there is a bug in the silicon. + case (MCP_STD): + mcp2515_modifyRegister(MCP_RXB0CTRL, + MCP_RXB_RX_MASK | MCP_RXB_BUKT_MASK, + MCP_RXB_RX_STD | MCP_RXB_BUKT_MASK ); + mcp2515_modifyRegister(MCP_RXB1CTRL, MCP_RXB_RX_MASK, + MCP_RXB_RX_STD); + break; + + case (MCP_EXT): + mcp2515_modifyRegister(MCP_RXB0CTRL, + MCP_RXB_RX_MASK | MCP_RXB_BUKT_MASK, + MCP_RXB_RX_EXT | MCP_RXB_BUKT_MASK ); + mcp2515_modifyRegister(MCP_RXB1CTRL, MCP_RXB_RX_MASK, + MCP_RXB_RX_EXT); + break; +*/ + case (MCP_STDEXT): + mcp2515_modifyRegister(MCP_RXB0CTRL, + MCP_RXB_RX_MASK | MCP_RXB_BUKT_MASK, + MCP_RXB_RX_STDEXT | MCP_RXB_BUKT_MASK ); + mcp2515_modifyRegister(MCP_RXB1CTRL, MCP_RXB_RX_MASK, + MCP_RXB_RX_STDEXT); + break; + + default: +#if DEBUG_MODE + Serial.println(F("`Setting ID Mode Failure...")); +#endif + return MCP2515_FAIL; + break; +} + + + res = mcp2515_setCANCTRL_Mode(mcpMode); if(res) { #if DEBUG_MODE - Serial.print("Enter Normal Mode Fall!!\r\n"); + Serial.println(F("Returning to Previous Mode Failure...")); #endif return res; } - -#if DEBUG_MODE - Serial.print("Enter Normal Mode Success!!\r\n"); -#endif - } return res; @@ -386,7 +651,7 @@ INT8U MCP_CAN::mcp2515_init(const INT8U canSpeed) /* mcp25 /********************************************************************************************************* ** Function name: mcp2515_write_id -** Descriptions: write can id +** Descriptions: Write CAN ID *********************************************************************************************************/ void MCP_CAN::mcp2515_write_id( const INT8U mcp_addr, const INT8U ext, const INT32U id ) { @@ -412,12 +677,46 @@ void MCP_CAN::mcp2515_write_id( const INT8U mcp_addr, const INT8U ext, const INT tbufdata[MCP_EID0] = 0; tbufdata[MCP_EID8] = 0; } + + mcp2515_setRegisterS( mcp_addr, tbufdata, 4 ); +} + +/********************************************************************************************************* +** Function name: mcp2515_write_mf +** Descriptions: Write Masks and Filters +*********************************************************************************************************/ +void MCP_CAN::mcp2515_write_mf( const INT8U mcp_addr, const INT8U ext, const INT32U id ) +{ + uint16_t canid; + INT8U tbufdata[4]; + + canid = (uint16_t)(id & 0x0FFFF); + + if ( ext == 1) + { + tbufdata[MCP_EID0] = (INT8U) (canid & 0xFF); + tbufdata[MCP_EID8] = (INT8U) (canid >> 8); + canid = (uint16_t)(id >> 16); + tbufdata[MCP_SIDL] = (INT8U) (canid & 0x03); + tbufdata[MCP_SIDL] += (INT8U) ((canid & 0x1C) << 3); + tbufdata[MCP_SIDL] |= MCP_TXB_EXIDE_M; + tbufdata[MCP_SIDH] = (INT8U) (canid >> 5 ); + } + else + { + tbufdata[MCP_EID0] = (INT8U) (canid & 0xFF); + tbufdata[MCP_EID8] = (INT8U) (canid >> 8); + canid = (uint16_t)(id >> 16); + tbufdata[MCP_SIDL] = (INT8U) ((canid & 0x07) << 5); + tbufdata[MCP_SIDH] = (INT8U) (canid >> 3 ); + } + mcp2515_setRegisterS( mcp_addr, tbufdata, 4 ); } /********************************************************************************************************* ** Function name: mcp2515_read_id -** Descriptions: read can id +** Descriptions: Read CAN ID *********************************************************************************************************/ void MCP_CAN::mcp2515_read_id( const INT8U mcp_addr, INT8U* ext, INT32U* id ) { @@ -442,25 +741,25 @@ void MCP_CAN::mcp2515_read_id( const INT8U mcp_addr, INT8U* ext, INT32U* id ) /********************************************************************************************************* ** Function name: mcp2515_write_canMsg -** Descriptions: write msg +** Descriptions: Write message *********************************************************************************************************/ void MCP_CAN::mcp2515_write_canMsg( const INT8U buffer_sidh_addr) { INT8U mcp_addr; mcp_addr = buffer_sidh_addr; mcp2515_setRegisterS(mcp_addr+5, m_nDta, m_nDlc ); /* write data bytes */ + if ( m_nRtr == 1) /* if RTR set bit in byte */ - { m_nDlc |= MCP_RTR_MASK; - } - mcp2515_setRegister((mcp_addr+4), m_nDlc ); /* write the RTR and DLC */ - mcp2515_write_id(mcp_addr, m_nExtFlg, m_nID ); /* write CAN id */ + + mcp2515_setRegister((mcp_addr+4), m_nDlc ); /* write the RTR and DLC */ + mcp2515_write_id(mcp_addr, m_nExtFlg, m_nID ); /* write CAN id */ } /********************************************************************************************************* ** Function name: mcp2515_read_canMsg -** Descriptions: read message +** Descriptions: Read message *********************************************************************************************************/ void MCP_CAN::mcp2515_read_canMsg( const INT8U buffer_sidh_addr) /* read can msg */ { @@ -473,29 +772,20 @@ void MCP_CAN::mcp2515_read_canMsg( const INT8U buffer_sidh_addr) /* read ctrl = mcp2515_readRegister( mcp_addr-1 ); m_nDlc = mcp2515_readRegister( mcp_addr+4 ); - if ((ctrl & 0x08)) { + if(m_nDlc > MAX_CHAR_IN_MESSAGE) m_nDlc = MAX_CHAR_IN_MESSAGE; + + if (ctrl & 0x08) m_nRtr = 1; - } - else { + else m_nRtr = 0; - } m_nDlc &= MCP_DLC_MASK; mcp2515_readRegisterS( mcp_addr+5, &(m_nDta[0]), m_nDlc ); } /********************************************************************************************************* -** Function name: sendMsg -** Descriptions: send message -*********************************************************************************************************/ -void MCP_CAN::mcp2515_start_transmit(const INT8U mcp_addr) /* start transmit */ -{ - mcp2515_modifyRegister( mcp_addr-1 , MCP_TXB_TXREQ_M, MCP_TXB_TXREQ_M ); -} - -/********************************************************************************************************* -** Function name: sendMsg -** Descriptions: send message +** Function name: mcp2515_getNextFreeTXBuf +** Descriptions: Send message *********************************************************************************************************/ INT8U MCP_CAN::mcp2515_getNextFreeTXBuf(INT8U *txbuf_n) /* get Next free txbuf */ { @@ -509,8 +799,8 @@ INT8U MCP_CAN::mcp2515_getNextFreeTXBuf(INT8U *txbuf_n) /* get N for (i=0; ibegin(); + res = mcp2515_init(idmodeset, speedset, clockset); + if (res == MCP2515_OK) + return CAN_OK; + + return CAN_FAILINIT; } /********************************************************************************************************* ** Function name: init_Mask -** Descriptions: init canid Masks +** Descriptions: Public function to set mask(s). *********************************************************************************************************/ INT8U MCP_CAN::init_Mask(INT8U num, INT8U ext, INT32U ulData) { INT8U res = MCP2515_OK; #if DEBUG_MODE - Serial.print("Begin to set Mask!!\r\n"); + Serial.println(F("Starting to Set Mask!")); +#endif + res = mcp2515_setCANCTRL_Mode(MODE_CONFIG); + if(res > 0){ +#if DEBUG_MODE + Serial.println(F("Entering Configuration Mode Failure...")); +#endif + return res; + } + + if (num == 0){ + mcp2515_write_mf(MCP_RXM0SIDH, ext, ulData); + + } + else if(num == 1){ + mcp2515_write_mf(MCP_RXM1SIDH, ext, ulData); + } + else res = MCP2515_FAIL; + + res = mcp2515_setCANCTRL_Mode(mcpMode); + if(res > 0){ +#if DEBUG_MODE + Serial.println(F("Entering Previous Mode Failure...")); + Serial.println(F("Setting Mask Failure...")); +#endif + return res; + } +#if DEBUG_MODE + Serial.println(F("Setting Mask Successful!")); +#endif + return res; +} + +/********************************************************************************************************* +** Function name: init_Mask +** Descriptions: Public function to set mask(s). +*********************************************************************************************************/ +INT8U MCP_CAN::init_Mask(INT8U num, INT32U ulData) +{ + INT8U res = MCP2515_OK; + INT8U ext = 0; +#if DEBUG_MODE + Serial.println(F("Starting to Set Mask!")); #endif res = mcp2515_setCANCTRL_Mode(MODE_CONFIG); if(res > 0){ #if DEBUG_MODE - Serial.print("Enter setting mode fall\r\n"); + Serial.println(F("Entering Configuration Mode Failure...")); #endif return res; } + if((ulData & 0x80000000) == 0x80000000) + ext = 1; + if (num == 0){ - mcp2515_write_id(MCP_RXM0SIDH, ext, ulData); + mcp2515_write_mf(MCP_RXM0SIDH, ext, ulData); } else if(num == 1){ - mcp2515_write_id(MCP_RXM1SIDH, ext, ulData); + mcp2515_write_mf(MCP_RXM1SIDH, ext, ulData); } else res = MCP2515_FAIL; - res = mcp2515_setCANCTRL_Mode(MODE_NORMAL); + res = mcp2515_setCANCTRL_Mode(mcpMode); if(res > 0){ #if DEBUG_MODE - Serial.print("Enter normal mode fall\r\n"); + Serial.println(F("Entering Previous Mode Failure...")); + Serial.println(F("Setting Mask Failure...")); #endif return res; } #if DEBUG_MODE - Serial.print("set Mask success!!\r\n"); + Serial.println(F("Setting Mask Successful!")); #endif return res; } /********************************************************************************************************* ** Function name: init_Filt -** Descriptions: init canid filters +** Descriptions: Public function to set filter(s). *********************************************************************************************************/ INT8U MCP_CAN::init_Filt(INT8U num, INT8U ext, INT32U ulData) { INT8U res = MCP2515_OK; #if DEBUG_MODE - Serial.print("Begin to set Filter!!\r\n"); + Serial.println(F("Starting to Set Filter!")); +#endif + res = mcp2515_setCANCTRL_Mode(MODE_CONFIG); + if(res > 0) + { +#if DEBUG_MODE + Serial.println(F("Enter Configuration Mode Failure...")); +#endif + return res; + } + + switch( num ) + { + case 0: + mcp2515_write_mf(MCP_RXF0SIDH, ext, ulData); + break; + + case 1: + mcp2515_write_mf(MCP_RXF1SIDH, ext, ulData); + break; + + case 2: + mcp2515_write_mf(MCP_RXF2SIDH, ext, ulData); + break; + + case 3: + mcp2515_write_mf(MCP_RXF3SIDH, ext, ulData); + break; + + case 4: + mcp2515_write_mf(MCP_RXF4SIDH, ext, ulData); + break; + + case 5: + mcp2515_write_mf(MCP_RXF5SIDH, ext, ulData); + break; + + default: + res = MCP2515_FAIL; + } + + res = mcp2515_setCANCTRL_Mode(mcpMode); + if(res > 0) + { +#if DEBUG_MODE + Serial.println(F("Entering Previous Mode Failure...")); + Serial.println(F("Setting Filter Failure...")); +#endif + return res; + } +#if DEBUG_MODE + Serial.println(F("Setting Filter Successful!")); +#endif + + return res; +} + +/********************************************************************************************************* +** Function name: init_Filt +** Descriptions: Public function to set filter(s). +*********************************************************************************************************/ +INT8U MCP_CAN::init_Filt(INT8U num, INT32U ulData) +{ + INT8U res = MCP2515_OK; + INT8U ext = 0; + +#if DEBUG_MODE + Serial.println(F("Starting to Set Filter!")); #endif res = mcp2515_setCANCTRL_Mode(MODE_CONFIG); if(res > 0) { #if DEBUG_MODE - Serial.print("Enter setting mode fall\r\n"); + Serial.println(F("Enter Configuration Mode Failure...")); #endif return res; } + if((ulData & 0x80000000) == 0x80000000) + ext = 1; + switch( num ) { case 0: - mcp2515_write_id(MCP_RXF0SIDH, ext, ulData); + mcp2515_write_mf(MCP_RXF0SIDH, ext, ulData); break; case 1: - mcp2515_write_id(MCP_RXF1SIDH, ext, ulData); + mcp2515_write_mf(MCP_RXF1SIDH, ext, ulData); break; case 2: - mcp2515_write_id(MCP_RXF2SIDH, ext, ulData); + mcp2515_write_mf(MCP_RXF2SIDH, ext, ulData); break; case 3: - mcp2515_write_id(MCP_RXF3SIDH, ext, ulData); + mcp2515_write_mf(MCP_RXF3SIDH, ext, ulData); break; case 4: - mcp2515_write_id(MCP_RXF4SIDH, ext, ulData); + mcp2515_write_mf(MCP_RXF4SIDH, ext, ulData); break; case 5: - mcp2515_write_id(MCP_RXF5SIDH, ext, ulData); + mcp2515_write_mf(MCP_RXF5SIDH, ext, ulData); break; default: res = MCP2515_FAIL; } - res = mcp2515_setCANCTRL_Mode(MODE_NORMAL); + res = mcp2515_setCANCTRL_Mode(mcpMode); if(res > 0) { #if DEBUG_MODE - Serial.print("Enter normal mode fall\r\nSet filter fail!!\r\n"); + Serial.println(F("Entering Previous Mode Failure...")); + Serial.println(F("Setting Filter Failure...")); #endif return res; } #if DEBUG_MODE - Serial.print("set Filter success!!\r\n"); + Serial.println(F("Setting Filter Successful!")); #endif return res; @@ -649,22 +1071,27 @@ INT8U MCP_CAN::init_Filt(INT8U num, INT8U ext, INT32U ulData) /********************************************************************************************************* ** Function name: setMsg -** Descriptions: set can message, such as dlc, id, dta[] and so on +** Descriptions: Set can message, such as dlc, id, dta[] and so on *********************************************************************************************************/ -INT8U MCP_CAN::setMsg(INT32U id, INT8U ext, INT8U len, INT8U *pData) +INT8U MCP_CAN::setMsg(INT32U id, INT8U rtr, INT8U ext, INT8U len, INT8U *pData) { int i = 0; - m_nExtFlg = ext; m_nID = id; + m_nRtr = rtr; + m_nExtFlg = ext; m_nDlc = len; - for(i = 0; i MAX_CHAR_IN_MESSAGE) m_nDlc = MAX_CHAR_IN_MESSAGE; + + for(i = 0; i= TIMEOUTVALUE) { return CAN_GETTXBFTIMEOUT; /* get tx buff time out */ } uiTimeOut = 0; mcp2515_write_canMsg( txbuf_n); - mcp2515_start_transmit( txbuf_n ); + mcp2515_modifyRegister( txbuf_n-1 , MCP_TXB_TXREQ_M, MCP_TXB_TXREQ_M ); + + temp = micros(); do - { - uiTimeOut++; - res1= mcp2515_readRegister(txbuf_n); /* read send buff ctrl reg */ - res1 = res1 & 0x08; - }while(res1 && (uiTimeOut < TIMEOUTVALUE)); - if(uiTimeOut == TIMEOUTVALUE) /* send msg timeout */ - { + { + res1 = mcp2515_readRegister(txbuf_n-1); /* read send buff ctrl reg */ + res1 = res1 & 0x08; + uiTimeOut = micros() - temp; + } while (res1 && (uiTimeOut < TIMEOUTVALUE)); + + if(uiTimeOut >= TIMEOUTVALUE) /* send msg timeout */ return CAN_SENDMSGTIMEOUT; - } + return CAN_OK; - } /********************************************************************************************************* ** Function name: sendMsgBuf -** Descriptions: send buf +** Descriptions: Send message to transmitt buffer *********************************************************************************************************/ INT8U MCP_CAN::sendMsgBuf(INT32U id, INT8U ext, INT8U len, INT8U *buf) { - setMsg(id, ext, len, buf); - sendMsg(); + INT8U res; + + setMsg(id, 0, ext, len, buf); + res = sendMsg(); + + return res; +} + +/********************************************************************************************************* +** Function name: sendMsgBuf +** Descriptions: Send message to transmitt buffer +*********************************************************************************************************/ +INT8U MCP_CAN::sendMsgBuf(INT32U id, INT8U len, INT8U *buf) +{ + INT8U ext = 0, rtr = 0; + INT8U res; + + if((id & 0x80000000) == 0x80000000) + ext = 1; + + if((id & 0x40000000) == 0x40000000) + rtr = 1; + + setMsg(id, rtr, ext, len, buf); + res = sendMsg(); + + return res; } /********************************************************************************************************* ** Function name: readMsg -** Descriptions: read message +** Descriptions: Read message *********************************************************************************************************/ INT8U MCP_CAN::readMsg() { @@ -747,70 +1202,175 @@ INT8U MCP_CAN::readMsg() res = CAN_OK; } else - { res = CAN_NOMSG; - } + return res; } /********************************************************************************************************* ** Function name: readMsgBuf -** Descriptions: read message buf +** Descriptions: Public function, Reads message from receive buffer. *********************************************************************************************************/ -INT8U MCP_CAN::readMsgBuf(INT8U *len, INT8U buf[]) +INT8U MCP_CAN::readMsgBuf(INT32U *id, INT8U *ext, INT8U *len, INT8U buf[]) { - readMsg(); + if(readMsg() == CAN_NOMSG) + return CAN_NOMSG; + + if(m_nDlc > MAX_CHAR_IN_MESSAGE) m_nDlc = MAX_CHAR_IN_MESSAGE; + + *id = m_nID; *len = m_nDlc; + *ext = m_nExtFlg; for(int i = 0; i MAX_CHAR_IN_MESSAGE) m_nDlc = MAX_CHAR_IN_MESSAGE; + + *id = m_nID; + *len = m_nDlc; + + for(int i = 0; i> 3); +} + /********************************************************************************************************* END FILE *********************************************************************************************************/ diff --git a/mcp_can.h b/mcp_can.h index ff2fd48..8c2d8ed 100644 --- a/mcp_can.h +++ b/mcp_can.h @@ -1,10 +1,11 @@ /* mcp_can.h 2012 Copyright (c) Seeed Technology Inc. All right reserved. + 2017 Copyright (c) Cory J. Fowler All Rights Reserved. Author:Loovee Contributor: Cory J. Fowler - 2014-1-16 + 2017-09-25 This library 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 @@ -30,83 +31,108 @@ class MCP_CAN { private: - INT8U m_nExtFlg; /* identifier xxxID */ - /* either extended (the 29 LSB) */ - /* or standard (the 11 LSB) */ - INT32U m_nID; /* can id */ - INT8U m_nDlc; /* data length: */ - INT8U m_nDta[MAX_CHAR_IN_MESSAGE]; /* data */ - INT8U m_nRtr; /* rtr */ - INT8U m_nfilhit; - INT8U SPICS; + INT8U m_nExtFlg; // Identifier Type + // Extended (29 bit) or Standard (11 bit) + INT32U m_nID; // CAN ID + INT8U m_nDlc; // Data Length Code + INT8U m_nDta[MAX_CHAR_IN_MESSAGE]; // Data array + INT8U m_nRtr; // Remote request flag + INT8U m_nfilhit; // The number of the filter that matched the message + SPIClass *mcpSPI; // The SPI-Device used + INT8U MCPCS; // Chip Select pin number + INT8U mcpMode; // Mode to return to after configurations are performed. + -/* -* mcp2515 driver function -*/ +/********************************************************************************************************* + * mcp2515 driver function + *********************************************************************************************************/ // private: private: - void mcp2515_reset(void); /* reset mcp2515 */ + void mcp2515_reset(void); // Soft Reset MCP2515 - INT8U mcp2515_readRegister(const INT8U address); /* read mcp2515's register */ + INT8U mcp2515_readRegister(const INT8U address); // Read MCP2515 register - void mcp2515_readRegisterS(const INT8U address, - INT8U values[], + void mcp2515_readRegisterS(const INT8U address, // Read MCP2515 successive registers + INT8U values[], const INT8U n); - void mcp2515_setRegister(const INT8U address, /* set mcp2515's register */ + + void mcp2515_setRegister(const INT8U address, // Set MCP2515 register const INT8U value); - void mcp2515_setRegisterS(const INT8U address, /* set mcp2515's registers */ + void mcp2515_setRegisterS(const INT8U address, // Set MCP2515 successive registers const INT8U values[], const INT8U n); - + void mcp2515_initCANBuffers(void); - - void mcp2515_modifyRegister(const INT8U address, /* set bit of one register */ + + void mcp2515_modifyRegister(const INT8U address, // Set specific bit(s) of a register const INT8U mask, const INT8U data); - INT8U mcp2515_readStatus(void); /* read mcp2515's Status */ - INT8U mcp2515_setCANCTRL_Mode(const INT8U newmode); /* set mode */ - INT8U mcp2515_configRate(const INT8U canSpeed); /* set boadrate */ - INT8U mcp2515_init(const INT8U canSpeed); /* mcp2515init */ + INT8U mcp2515_readStatus(void); // Read MCP2515 Status + INT8U mcp2515_setCANCTRL_Mode(const INT8U newmode); // Set mode + INT8U mcp2515_requestNewMode(const INT8U newmode); // Set mode + INT8U mcp2515_configRate(const INT8U canSpeed, // Set baudrate - void mcp2515_write_id( const INT8U mcp_addr, /* write can id */ - const INT8U ext, - const INT32U id ); + const INT8U canClock); + + INT8U mcp2515_init(const INT8U canIDMode, // Initialize Controller + const INT8U canSpeed, + const INT8U canClock); - void mcp2515_read_id( const INT8U mcp_addr, /* read can id */ - INT8U* ext, - INT32U* id ); + void mcp2515_write_mf( const INT8U mcp_addr, // Write CAN Mask or Filter + const INT8U ext, + const INT32U id ); - void mcp2515_write_canMsg( const INT8U buffer_sidh_addr ); /* write can msg */ - void mcp2515_read_canMsg( const INT8U buffer_sidh_addr); /* read can msg */ - void mcp2515_start_transmit(const INT8U mcp_addr); /* start transmit */ - INT8U mcp2515_getNextFreeTXBuf(INT8U *txbuf_n); /* get Next free txbuf */ + void mcp2515_write_id( const INT8U mcp_addr, // Write CAN ID + const INT8U ext, + const INT32U id ); -/* -* can operator function -*/ + void mcp2515_read_id( const INT8U mcp_addr, // Read CAN ID + INT8U* ext, + INT32U* id ); + + void mcp2515_write_canMsg( const INT8U buffer_sidh_addr ); // Write CAN message + void mcp2515_read_canMsg( const INT8U buffer_sidh_addr); // Read CAN message + INT8U mcp2515_getNextFreeTXBuf(INT8U *txbuf_n); // Find empty transmit buffer + +/********************************************************************************************************* + * CAN operator function + *********************************************************************************************************/ - INT8U setMsg(INT32U id, INT8U ext, INT8U len, INT8U *pData); /* set message */ - INT8U clearMsg(); /* clear all message to zero */ - INT8U readMsg(); /* read message */ - INT8U sendMsg(); /* send message */ + INT8U setMsg(INT32U id, INT8U rtr, INT8U ext, INT8U len, INT8U *pData); // Set message + INT8U clearMsg(); // Clear all message to zero + INT8U readMsg(); // Read message + INT8U sendMsg(); // Send message public: MCP_CAN(INT8U _CS); - INT8U begin(INT8U speedset); /* init can */ - INT8U init_Mask(INT8U num, INT8U ext, INT32U ulData); /* init Masks */ - INT8U init_Filt(INT8U num, INT8U ext, INT32U ulData); /* init filters */ - INT8U sendMsgBuf(INT32U id, INT8U ext, INT8U len, INT8U *buf); /* send buf */ - INT8U readMsgBuf(INT8U *len, INT8U *buf); /* read buf */ - INT8U checkReceive(void); /* if something received */ - INT8U checkError(void); /* if something error */ - INT32U getCanId(void); /* get can id when receive */ + MCP_CAN(SPIClass *_SPI, INT8U _CS); + INT8U begin(INT8U idmodeset, INT8U speedset, INT8U clockset); // Initialize controller parameters + INT8U init_Mask(INT8U num, INT8U ext, INT32U ulData); // Initialize Mask(s) + INT8U init_Mask(INT8U num, INT32U ulData); // Initialize Mask(s) + INT8U init_Filt(INT8U num, INT8U ext, INT32U ulData); // Initialize Filter(s) + INT8U init_Filt(INT8U num, INT32U ulData); // Initialize Filter(s) + void setSleepWakeup(INT8U enable); // Enable or disable the wake up interrupt (If disabled the MCP2515 will not be woken up by CAN bus activity) + INT8U setMode(INT8U opMode); // Set operational mode + INT8U sendMsgBuf(INT32U id, INT8U ext, INT8U len, INT8U *buf); // Send message to transmit buffer + INT8U sendMsgBuf(INT32U id, INT8U len, INT8U *buf); // Send message to transmit buffer + INT8U readMsgBuf(INT32U *id, INT8U *ext, INT8U *len, INT8U *buf); // Read message from receive buffer + INT8U readMsgBuf(INT32U *id, INT8U *len, INT8U *buf); // Read message from receive buffer + INT8U checkReceive(void); // Check for received data + INT8U checkError(void); // Check for errors + INT8U getError(void); // Check for errors + INT8U errorCountRX(void); // Get error count + INT8U errorCountTX(void); // Get error count + INT8U enOneShotTX(void); // Enable one-shot transmission + INT8U disOneShotTX(void); // Disable one-shot transmission + INT8U abortTX(void); // Abort queued transmission(s) + INT8U setGPO(INT8U data); // Sets GPO + INT8U getGPI(void); // Reads GPI }; -extern MCP_CAN CAN; #endif /********************************************************************************************************* - END FILE -*********************************************************************************************************/ + * END FILE + *********************************************************************************************************/ diff --git a/mcp_can_dfs.h b/mcp_can_dfs.h index 9dc0140..4e42b33 100644 --- a/mcp_can_dfs.h +++ b/mcp_can_dfs.h @@ -1,10 +1,11 @@ /* mcp_can_dfs.h 2012 Copyright (c) Seeed Technology Inc. All right reserved. + 2017 Copyright (c) Cory J. Fowler All Rights Reserved. Author:Loovee Contributor: Cory J. Fowler - 2014-1-16 + 2017-09-25 This library 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 @@ -36,12 +37,14 @@ #endif // if print debug information -#define DEBUG_MODE 0 +#ifndef DEBUG_MODE +#define DEBUG_MODE 1 +#endif /* * Begin mt */ -#define TIMEOUTVALUE 50 +#define TIMEOUTVALUE 2500 /* In Microseconds, May need changed depending on application and baud rate */ #define MCP_SIDH 0 #define MCP_SIDL 1 #define MCP_EID8 2 @@ -74,24 +77,29 @@ #define MCP_RXB_RTR_M 0x40 /* In RXBnDLC */ #define MCP_STAT_RXIF_MASK (0x03) -#define MCP_STAT_RX0IF (1<<0) -#define MCP_STAT_RX1IF (1<<1) - -#define MCP_EFLG_RX1OVR (1<<7) -#define MCP_EFLG_RX0OVR (1<<6) -#define MCP_EFLG_TXBO (1<<5) -#define MCP_EFLG_TXEP (1<<4) -#define MCP_EFLG_RXEP (1<<3) -#define MCP_EFLG_TXWAR (1<<2) -#define MCP_EFLG_RXWAR (1<<1) -#define MCP_EFLG_EWARN (1<<0) +#define MCP_STAT_RX0IF (1<<0) +#define MCP_STAT_RX1IF (1<<1) + +#define MCP_EFLG_RX1OVR (1<<7) +#define MCP_EFLG_RX0OVR (1<<6) +#define MCP_EFLG_TXBO (1<<5) +#define MCP_EFLG_TXEP (1<<4) +#define MCP_EFLG_RXEP (1<<3) +#define MCP_EFLG_TXWAR (1<<2) +#define MCP_EFLG_RXWAR (1<<1) +#define MCP_EFLG_EWARN (1<<0) #define MCP_EFLG_ERRORMASK (0xF8) /* 5 MS-Bits */ +#define MCP_BxBFS_MASK 0x30 +#define MCP_BxBFE_MASK 0x0C +#define MCP_BxBFM_MASK 0x03 + +#define MCP_BxRTS_MASK 0x38 +#define MCP_BxRTSM_MASK 0x07 /* * Define MCP2515 register addresses */ - #define MCP_RXF0SIDH 0x00 #define MCP_RXF0SIDL 0x01 #define MCP_RXF0EID8 0x02 @@ -104,6 +112,8 @@ #define MCP_RXF2SIDL 0x09 #define MCP_RXF2EID8 0x0A #define MCP_RXF2EID0 0x0B +#define MCP_BFPCTRL 0x0C +#define MCP_TXRTSCTRL 0x0D #define MCP_CANSTAT 0x0E #define MCP_CANCTRL 0x0F #define MCP_RXF3SIDH 0x10 @@ -143,10 +153,10 @@ #define MCP_RXB1SIDH 0x71 -#define MCP_TX_INT 0x1C // Enable all transmit interrup ts -#define MCP_TX01_INT 0x0C // Enable TXB0 and TXB1 interru pts -#define MCP_RX_INT 0x03 // Enable receive interrupts -#define MCP_NO_INT 0x00 // Disable all interrupts +#define MCP_TX_INT 0x1C /* Enable all transmit interrupts */ +#define MCP_TX01_INT 0x0C /* Enable TXB0 and TXB1 interrupts */ +#define MCP_RX_INT 0x03 /* Enable receive interrupts */ +#define MCP_NO_INT 0x00 /* Disable all interrupts */ #define MCP_TX01_MASK 0x14 #define MCP_TX_MASK 0x54 @@ -154,7 +164,6 @@ /* * Define SPI Instruction Set */ - #define MCP_WRITE 0x02 #define MCP_READ 0x03 @@ -183,11 +192,10 @@ /* * CANCTRL Register Values */ - -#define MODE_NORMAL 0x00 -#define MODE_SLEEP 0x20 -#define MODE_LOOPBACK 0x40 -#define MODE_LISTENONLY 0x60 +#define MCP_NORMAL 0x00 +#define MCP_SLEEP 0x20 +#define MCP_LOOPBACK 0x40 +#define MCP_LISTENONLY 0x60 #define MODE_CONFIG 0x80 #define MODE_POWERUP 0xE0 #define MODE_MASK 0xE0 @@ -204,7 +212,6 @@ /* * CNF1 Register Values */ - #define SJW1 0x00 #define SJW2 0x40 #define SJW3 0x80 @@ -214,7 +221,6 @@ /* * CNF2 Register Values */ - #define BTLMODE 0x80 #define SAMPLE_1X 0x00 #define SAMPLE_3X 0x40 @@ -223,7 +229,6 @@ /* * CNF3 Register Values */ - #define SOF_ENABLE 0x80 #define SOF_DISABLE 0x00 #define WAKFIL_ENABLE 0x40 @@ -233,7 +238,6 @@ /* * CANINTF Register Bits */ - #define MCP_RX0IF 0x01 #define MCP_RX1IF 0x02 #define MCP_TX0IF 0x04 @@ -243,61 +247,159 @@ #define MCP_WAKIF 0x40 #define MCP_MERRF 0x80 + +/* + * Speed 8M + */ +#define MCP_8MHz_1000kBPS_CFG1 (0x00) +#define MCP_8MHz_1000kBPS_CFG2 (0xC0) /* Enabled SAM bit */ +#define MCP_8MHz_1000kBPS_CFG3 (0x80) /* Sample point at 75% */ + +#define MCP_8MHz_500kBPS_CFG1 (0x00) +#define MCP_8MHz_500kBPS_CFG2 (0xD1) /* Enabled SAM bit */ +#define MCP_8MHz_500kBPS_CFG3 (0x81) /* Sample point at 75% */ + +#define MCP_8MHz_250kBPS_CFG1 (0x80) /* Increased SJW */ +#define MCP_8MHz_250kBPS_CFG2 (0xE5) /* Enabled SAM bit */ +#define MCP_8MHz_250kBPS_CFG3 (0x83) /* Sample point at 75% */ + +#define MCP_8MHz_200kBPS_CFG1 (0x80) /* Increased SJW */ +#define MCP_8MHz_200kBPS_CFG2 (0xF6) /* Enabled SAM bit */ +#define MCP_8MHz_200kBPS_CFG3 (0x84) /* Sample point at 75% */ + +#define MCP_8MHz_125kBPS_CFG1 (0x81) /* Increased SJW */ +#define MCP_8MHz_125kBPS_CFG2 (0xE5) /* Enabled SAM bit */ +#define MCP_8MHz_125kBPS_CFG3 (0x83) /* Sample point at 75% */ + +#define MCP_8MHz_100kBPS_CFG1 (0x81) /* Increased SJW */ +#define MCP_8MHz_100kBPS_CFG2 (0xF6) /* Enabled SAM bit */ +#define MCP_8MHz_100kBPS_CFG3 (0x84) /* Sample point at 75% */ + +#define MCP_8MHz_80kBPS_CFG1 (0x84) /* Increased SJW */ +#define MCP_8MHz_80kBPS_CFG2 (0xD3) /* Enabled SAM bit */ +#define MCP_8MHz_80kBPS_CFG3 (0x81) /* Sample point at 75% */ + +#define MCP_8MHz_50kBPS_CFG1 (0x84) /* Increased SJW */ +#define MCP_8MHz_50kBPS_CFG2 (0xE5) /* Enabled SAM bit */ +#define MCP_8MHz_50kBPS_CFG3 (0x83) /* Sample point at 75% */ + +#define MCP_8MHz_40kBPS_CFG1 (0x84) /* Increased SJW */ +#define MCP_8MHz_40kBPS_CFG2 (0xF6) /* Enabled SAM bit */ +#define MCP_8MHz_40kBPS_CFG3 (0x84) /* Sample point at 75% */ + +#define MCP_8MHz_33k3BPS_CFG1 (0x85) /* Increased SJW */ +#define MCP_8MHz_33k3BPS_CFG2 (0xF6) /* Enabled SAM bit */ +#define MCP_8MHz_33k3BPS_CFG3 (0x84) /* Sample point at 75% */ + +#define MCP_8MHz_31k25BPS_CFG1 (0x87) /* Increased SJW */ +#define MCP_8MHz_31k25BPS_CFG2 (0xE5) /* Enabled SAM bit */ +#define MCP_8MHz_31k25BPS_CFG3 (0x83) /* Sample point at 75% */ + +#define MCP_8MHz_20kBPS_CFG1 (0x89) /* Increased SJW */ +#define MCP_8MHz_20kBPS_CFG2 (0xF6) /* Enabled SAM bit */ +#define MCP_8MHz_20kBPS_CFG3 (0x84) /* Sample point at 75% */ + +#define MCP_8MHz_10kBPS_CFG1 (0x93) /* Increased SJW */ +#define MCP_8MHz_10kBPS_CFG2 (0xF6) /* Enabled SAM bit */ +#define MCP_8MHz_10kBPS_CFG3 (0x84) /* Sample point at 75% */ + +#define MCP_8MHz_5kBPS_CFG1 (0xA7) /* Increased SJW */ +#define MCP_8MHz_5kBPS_CFG2 (0xF6) /* Enabled SAM bit */ +#define MCP_8MHz_5kBPS_CFG3 (0x84) /* Sample point at 75% */ + /* * speed 16M */ #define MCP_16MHz_1000kBPS_CFG1 (0x00) -#define MCP_16MHz_1000kBPS_CFG2 (0xD0) -#define MCP_16MHz_1000kBPS_CFG3 (0x82) +#define MCP_16MHz_1000kBPS_CFG2 (0xCA) +#define MCP_16MHz_1000kBPS_CFG3 (0x81) /* Sample point at 75% */ -#define MCP_16MHz_500kBPS_CFG1 (0x00) -#define MCP_16MHz_500kBPS_CFG2 (0xF0) -#define MCP_16MHz_500kBPS_CFG3 (0x86) +#define MCP_16MHz_500kBPS_CFG1 (0x40) /* Increased SJW */ +#define MCP_16MHz_500kBPS_CFG2 (0xE5) +#define MCP_16MHz_500kBPS_CFG3 (0x83) /* Sample point at 75% */ #define MCP_16MHz_250kBPS_CFG1 (0x41) -#define MCP_16MHz_250kBPS_CFG2 (0xF1) -#define MCP_16MHz_250kBPS_CFG3 (0x85) +#define MCP_16MHz_250kBPS_CFG2 (0xE5) +#define MCP_16MHz_250kBPS_CFG3 (0x83) /* Sample point at 75% */ -#define MCP_16MHz_200kBPS_CFG1 (0x01) -#define MCP_16MHz_200kBPS_CFG2 (0xFA) -#define MCP_16MHz_200kBPS_CFG3 (0x87) +#define MCP_16MHz_200kBPS_CFG1 (0x41) /* Increased SJW */ +#define MCP_16MHz_200kBPS_CFG2 (0xF6) +#define MCP_16MHz_200kBPS_CFG3 (0x84) /* Sample point at 75% */ -#define MCP_16MHz_125kBPS_CFG1 (0x03) -#define MCP_16MHz_125kBPS_CFG2 (0xF0) -#define MCP_16MHz_125kBPS_CFG3 (0x86) +#define MCP_16MHz_125kBPS_CFG1 (0x43) /* Increased SJW */ +#define MCP_16MHz_125kBPS_CFG2 (0xE5) +#define MCP_16MHz_125kBPS_CFG3 (0x83) /* Sample point at 75% */ -#define MCP_16MHz_100kBPS_CFG1 (0x03) -#define MCP_16MHz_100kBPS_CFG2 (0xFA) -#define MCP_16MHz_100kBPS_CFG3 (0x87) +#define MCP_16MHz_100kBPS_CFG1 (0x44) /* Increased SJW */ +#define MCP_16MHz_100kBPS_CFG2 (0xE5) +#define MCP_16MHz_100kBPS_CFG3 (0x83) /* Sample point at 75% */ -#define MCP_16MHz_80kBPS_CFG1 (0x03) -#define MCP_16MHz_80kBPS_CFG2 (0xFF) -#define MCP_16MHz_80kBPS_CFG3 (0x87) +#define MCP_16MHz_80kBPS_CFG1 (0x44) /* Increased SJW */ +#define MCP_16MHz_80kBPS_CFG2 (0xF6) +#define MCP_16MHz_80kBPS_CFG3 (0x84) /* Sample point at 75% */ -#define MCP_16MHz_50kBPS_CFG1 (0x07) -#define MCP_16MHz_50kBPS_CFG2 (0xFA) -#define MCP_16MHz_50kBPS_CFG3 (0x87) +#define MCP_16MHz_50kBPS_CFG1 (0x47) /* Increased SJW */ +#define MCP_16MHz_50kBPS_CFG2 (0xF6) +#define MCP_16MHz_50kBPS_CFG3 (0x84) /* Sample point at 75% */ -#define MCP_16MHz_40kBPS_CFG1 (0x07) -#define MCP_16MHz_40kBPS_CFG2 (0xFF) -#define MCP_16MHz_40kBPS_CFG3 (0x87) +#define MCP_16MHz_40kBPS_CFG1 (0x49) /* Increased SJW */ +#define MCP_16MHz_40kBPS_CFG2 (0xF6) +#define MCP_16MHz_40kBPS_CFG3 (0x84) /* Sample point at 75% */ -#define MCP_16MHz_31k25BPS_CFG1 (0x0F) -#define MCP_16MHz_31k25BPS_CFG2 (0xF1) -#define MCP_16MHz_31k25BPS_CFG3 (0x85) +#define MCP_16MHz_33k3BPS_CFG1 (0x4E) +#define MCP_16MHz_33k3BPS_CFG2 (0xE5) +#define MCP_16MHz_33k3BPS_CFG3 (0x83) /* Sample point at 75% */ -#define MCP_16MHz_20kBPS_CFG1 (0x0F) -#define MCP_16MHz_20kBPS_CFG2 (0xFF) -#define MCP_16MHz_20kBPS_CFG3 (0x87) +#define MCP_16MHz_20kBPS_CFG1 (0x53) /* Increased SJW */ +#define MCP_16MHz_20kBPS_CFG2 (0xF6) +#define MCP_16MHz_20kBPS_CFG3 (0x84) /* Sample point at 75% */ -#define MCP_16MHz_10kBPS_CFG1 (0x1F) -#define MCP_16MHz_10kBPS_CFG2 (0xFF) -#define MCP_16MHz_10kBPS_CFG3 (0x87) +#define MCP_16MHz_10kBPS_CFG1 (0x67) /* Increased SJW */ +#define MCP_16MHz_10kBPS_CFG2 (0xF6) +#define MCP_16MHz_10kBPS_CFG3 (0x84) /* Sample point at 75% */ #define MCP_16MHz_5kBPS_CFG1 (0x3F) #define MCP_16MHz_5kBPS_CFG2 (0xFF) -#define MCP_16MHz_5kBPS_CFG3 (0x87) +#define MCP_16MHz_5kBPS_CFG3 (0x87) /* Sample point at 68% */ + +/* + * speed 20M + */ +#define MCP_20MHz_1000kBPS_CFG1 (0x00) +#define MCP_20MHz_1000kBPS_CFG2 (0xD9) +#define MCP_20MHz_1000kBPS_CFG3 (0x82) /* Sample point at 80% */ + +#define MCP_20MHz_500kBPS_CFG1 (0x40) /* Increased SJW */ +#define MCP_20MHz_500kBPS_CFG2 (0xF6) +#define MCP_20MHz_500kBPS_CFG3 (0x84) /* Sample point at 75% */ +#define MCP_20MHz_250kBPS_CFG1 (0x41) /* Increased SJW */ +#define MCP_20MHz_250kBPS_CFG2 (0xF6) +#define MCP_20MHz_250kBPS_CFG3 (0x84) /* Sample point at 75% */ + +#define MCP_20MHz_200kBPS_CFG1 (0x44) /* Increased SJW */ +#define MCP_20MHz_200kBPS_CFG2 (0xD3) +#define MCP_20MHz_200kBPS_CFG3 (0x81) /* Sample point at 80% */ + +#define MCP_20MHz_125kBPS_CFG1 (0x44) /* Increased SJW */ +#define MCP_20MHz_125kBPS_CFG2 (0xE5) +#define MCP_20MHz_125kBPS_CFG3 (0x83) /* Sample point at 75% */ + +#define MCP_20MHz_100kBPS_CFG1 (0x44) /* Increased SJW */ +#define MCP_20MHz_100kBPS_CFG2 (0xF6) +#define MCP_20MHz_100kBPS_CFG3 (0x84) /* Sample point at 75% */ + +#define MCP_20MHz_80kBPS_CFG1 (0xC4) /* Increased SJW */ +#define MCP_20MHz_80kBPS_CFG2 (0xFF) +#define MCP_20MHz_80kBPS_CFG3 (0x87) /* Sample point at 68% */ + +#define MCP_20MHz_50kBPS_CFG1 (0x49) /* Increased SJW */ +#define MCP_20MHz_50kBPS_CFG2 (0xF6) +#define MCP_20MHz_50kBPS_CFG3 (0x84) /* Sample point at 75% */ + +#define MCP_20MHz_40kBPS_CFG1 (0x18) +#define MCP_20MHz_40kBPS_CFG2 (0xD3) +#define MCP_20MHz_40kBPS_CFG3 (0x81) /* Sample point at 80% */ #define MCPDEBUG (0) @@ -307,9 +409,8 @@ #define MCP_RXBUF_0 (MCP_RXB0SIDH) #define MCP_RXBUF_1 (MCP_RXB1SIDH) -//#define SPICS 10 -#define MCP2515_SELECT() digitalWrite(SPICS, LOW) -#define MCP2515_UNSELECT() digitalWrite(SPICS, HIGH) +#define MCP2515_SELECT() digitalWrite(MCPCS, LOW) +#define MCP2515_UNSELECT() digitalWrite(MCPCS, HIGH) #define MCP2515_OK (0) #define MCP2515_FAIL (1) @@ -334,32 +435,50 @@ #define CANDEFAULTIDENT (0x55CC) #define CANDEFAULTIDENTEXT (CAN_EXTID) +#define MCP_STDEXT 0 /* Standard and Extended */ +#define MCP_STD 1 /* Standard IDs ONLY */ +#define MCP_EXT 2 /* Extended IDs ONLY */ +#define MCP_ANY 3 /* Disables Masks and Filters */ + +#define MCP_20MHZ 0 +#define MCP_16MHZ 1 +#define MCP_8MHZ 2 +#define MCP_CLOCK_SELECT 3 +#define MCP_CLKOUT_ENABLE 4 + + +#define CAN_4K096BPS 0 #define CAN_5KBPS 1 #define CAN_10KBPS 2 #define CAN_20KBPS 3 #define CAN_31K25BPS 4 -#define CAN_40KBPS 5 -#define CAN_50KBPS 6 -#define CAN_80KBPS 7 -#define CAN_100KBPS 8 -#define CAN_125KBPS 9 -#define CAN_200KBPS 10 -#define CAN_250KBPS 11 -#define CAN_500KBPS 12 -#define CAN_1000KBPS 13 - -#define CAN_OK (0) -#define CAN_FAILINIT (1) -#define CAN_FAILTX (2) -#define CAN_MSGAVAIL (3) -#define CAN_NOMSG (4) -#define CAN_CTRLERROR (5) +#define CAN_33K3BPS 5 +#define CAN_40KBPS 6 +#define CAN_50KBPS 7 +#define CAN_80KBPS 8 +#define CAN_100KBPS 9 +#define CAN_125KBPS 10 +#define CAN_200KBPS 11 +#define CAN_250KBPS 12 +#define CAN_500KBPS 13 +#define CAN_1000KBPS 14 + +#define CAN_OK (0) +#define CAN_FAILINIT (1) +#define CAN_FAILTX (2) +#define CAN_MSGAVAIL (3) +#define CAN_NOMSG (4) +#define CAN_CTRLERROR (5) #define CAN_GETTXBFTIMEOUT (6) #define CAN_SENDMSGTIMEOUT (7) #define CAN_FAIL (0xff) #define CAN_MAX_CHAR_IN_MESSAGE (8) +#define CAN_IS_EXTENDED 0x80000000 +#define CAN_IS_REMOTE_REQUEST 0x40000000 +#define CAN_EXTENDED_ID 0x1FFFFFFF + #endif /********************************************************************************************************* END FILE