Faster MCP2515 Interface - Part 1 - Embedded Micro Software

Embedded Micro Software
Go to content

.: Order using our PayPal powered shopping cart (PayPal account not required).

CanWiser Lite : $25

CAN232 : $100
I2C232 : $90

U.S. Shipping Addresses Only.
Sorry we do not ship Internationally.

View Online Shopping Cart





Faster MCP2515 Interface - Part 1

In this project we're adding a few functions to speed up the interface to the MCP2515 Controller Area Network (CAN) IC from Microchip. This is part 1 of a 2 part series.

In the last episode I laid out and had built for me the CanWiser Lite module which uses the Seeed Development XIAO ESP32C3 module connected to a MicroChip MCP2515 CAN IC. When I started writing software for it using the "mcp_can" library I realized that some new functions could be added to speed up the communications via the SPI channel.



This project details the faster functions.




The board I had built and assembled had a couple of defects. I forgot the pull up resistor on the reset line and also found out the ESP32C3 doesn't like 5 volt inputs. So I redid the board adding a pullup resistor and changed the MCP2515 voltage from 5 volts to 3.3 volts. It worked!
Now let's get to the software.

Start with a new sketch and add a timer highlighted in yellow.



Next add the timer code to the setup() function.

// timerInterrupt to run every 100 microseconds
// Use 1st timer of 4 (counted from zero).
// Set 80 divider for prescaler (see ESP32 Technical Reference Manual for more
// info).
timer = timerBegin(0, 80, true);

// Attach onTimer function to our timer.
timerAttachInterrupt(timer, &onTimer, true);

// Set alarm to call onTimer function every .1 milliseconds (value in microseconds).
// Repeat the alarm (third )
timerAlarmWrite(timer, 100, true);

timerAlarmEnable( timer );
And then add the timer interrupt ISR function before the setup() function.

uint8_t timer1ms;
volatile uint8_t timer1msTriggered;

void ARDUINO_ISR_ATTR onTimer( void )
{
 // ----------------------------------------------------
 // 1 millisecond timer count
 // ----------------------------------------------------
 timer1ms++;
 if( timer1ms >= 10 )
 {
    timer1ms = 0;
    timer1msTriggered = true;
 }
}

Notice in the onTimer() function the timer1ms counter is incremented until it reaches 10, then reset to 0, and a 1 millisecond flag is set. This is used in the loop() function as a scheduler.

In the loop() function the 1 millisecond flag is checked and if set it is reset to false and for now the LED pin is toggled.



Using a scope there appears to be "something" causing the 1 millisecond timer to be held off.
I'm not sure what causes this but if the loop() function is prevented from returning with a for loop the issue goes away.




Now we'll add the mcp_can library. The interface to the MCP2515 uses the SPI so both the mcp_can.h and SPI.h header files are included. The chip select pin for talking to the MCP2515 is D6 and the "interrupt" pin is D5. The MCP_CAN class is used and we use it as Can0. The following is added to the beginning of the file.

#include <SPI.h>
#include <mcp_can.h>

#define CAN0_INT D5   // Set INT to pin 5
#define CAN0_CS  D6
MCP_CAN Can0(CAN0_CS);     // Set CS to pin 6

Next we add the following to the setup() function. Notice that the serial channel has been added as well.

Serial.begin(115200);
 while(!Serial) { }
   
 /* Setup SPI access */
 SPI.begin();
 SPI.setClockDivider( SPI_CLOCK_DIV2 );
 
 pinMode(CAN0_INT, INPUT);
 // 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_8MHZ) == CAN_OK) Serial.println("MCP2515 Initialized Successfully!");
 else Serial.println("Error Initializing MCP2515...");

 Can0.setMode(MCP_NORMAL);  

In the loop() function when the 1 millisecond timer triggers a CAN message is sent using the Can0.sendMsgBuf() function. The LED is set high before this function is executed and then set low afterwards so we can use a scope plot to measure how long this function takes.

for(;; )
 {
 if( timer1msTriggered == true )
 {
   timer1msTriggered = false;
   uint8_t data[] = {1,2,3,4,5,6,7,8};
   digitalWrite( LED, HIGH );
   byte sndStat = Can0.sendMsgBuf(0x23, 0, 8, data);
   digitalWrite( LED, LOW );
 }   
 }

From the scope plot the time to send a CAN message using the mcp_can library is 360 microseconds. This is fine of course but I challenged myself to improve on this which will be discussed in Part 2. Stay tuned and thanks for showing an interest in this!











Back to content