A follow up project with the Arduino MKR CAN Shield.

In this post, I try to offload as much effort as possible to the CAN IC, and reduce the load on the Arduino microcontroller.

 

CAN Receive with Interrupts

 

The CAN controller, the Microchip MCP2515Microchip MCP2515, has several features that handle traffic management in hardware.

The first one I review here, the read interrupt, takes care that the controller doesn't have to check if data is arriving.

Instead of looping and polling all the time, the Arduino can spend its precious resources on other activities, or can go to a low power mode.

When traffic arrives, the CAN controller sends a signal on its trigger pin.

That pin is watched by the Arduino (the CAN library from Sandeep Mistry takes care of that).

 

My task, as user of the library, is to:

  • inform that I want to use the interrupt, and
  • provide an interrupt service handler.

 

The interrupt is activated by adding this piece of code:

 

  // register the receive callback
  CAN.onReceive(onReceive);

 

The interrupt function will be called when data arrives on the bus:

 

void onReceive(int packetSize) {
  // received a packet
  Serial.print("Received ");

  if (CAN.packetExtended()) {
    Serial.print("extended ");
  }

  if (CAN.packetRtr()) {
    // Remote transmission request, packet contains no data
    Serial.print("RTR ");
  }

  Serial.print("packet with id 0x");
  Serial.print(CAN.packetId(), HEX);

  if (CAN.packetRtr()) {
    Serial.print(" and requested length ");
    Serial.println(CAN.packetDlc());
  } else {
    Serial.print(" and length ");
    Serial.println(packetSize);

    // only print packet data for non-RTR packets
    while (CAN.available()) {
      Serial.print((char)CAN.read());
    }
    Serial.println();
  }

  Serial.println();
}

 

This isn't my code. It's the CANReceiverCallback example from the library I'm using.

In that example, the loop() method does nothing. That's because it doesn't have to do anything. All is handled by the interrupt.

 

void loop() {
  // do nothing
}

 

This is an excellent way to prove that the mechanism works.

In a real project, you may want to minimise the logic in the interrupt handler, and handle the majority of the processing in the loop().

That, in combination with sleeping whenever possible, can result in a well-behaving, low power design.

 

Add Hardware Filtering

 

The CAN controller has an additional hardware feature. It can filter inbound messages and only pass them to the Arduino if they match the filter's scope.

Together with the interrupt, this is a powerful combination.

Ther microcontroller just has to read the data. It already knows that that data is intended for it.

 

CAN BUS on Arduino MKR with interrupt and filter in hardware

 

In the first section, the microcontroller received an interrupt for each message appearing on the bus. It had to check the message ID of each data chunk and decide if it was of interest.

If the message wasn't intended for our design, we spent precious clock ticks to find that out. And if we were in low power mode, we woke up for nothing.

In this example with filters, the hardware capabilities of the CAN controller are put to optimal use. The controller only has to spend clock ticks if the message is relevant.

 

The change to the code is minimal. Add this snippet before the interrupt handler registration:

 

  // Set ID Filter 
  if (!CAN.filter(0x01)) {
    Serial.println("Setting CAN ID filter failed!");
    while (1);
  }

 

The screen capture above shows the project in action.

I used the Microchip CAN AnalyzerMicrochip CAN Analyzer to inject messages on the bus.

What you see on the image is the result of shooting two in-scope messages. Not shown, but tested by me, is that messages with a different ID are nicely ignored.

 

Road Test Blog
part 1: First trials
part 2: Inject CAN Messages
part 3: Analyzer as Test Tool
part 4: Analyze the Physical layer of CAN Bus
Related Blog
part 1: tryout
part 2: Communication between 2 Devices
part 3a: Design a Bus Driver PCB
part 3b: Design a Bus Driver PCB - Schematics and Custom Components
part 3c: Design a Bus Driver PCB - Layout
part 3d: Design a Bus Driver PCB - Test
Arduino MKR CAN Shield Review - part 1: Log BUS Traffic
Arduino MKR CAN Shield Review - part 2: Interrupt and Filter