MCP2515/2551 Setup w/Atmega2560

I have an MCP2515/2551 setup on a breadboard that’s identical to this circuit: https://github.com/autowp/arduino-mcp2515#send-data

Actually, I’ve had problems with this board in the past but they are resolved now (https://forum.arduino.cc/index.php?topic=637886.0), so this board is a good benchmark for testing out sending CAN messages. I can probe a message using the example sketch from the SPI bus, to the interface between the two ICs, to the CAN bus in the end eventually.

Now, I have another circuit (actually, the target PCB for the breadboard test circuit), that is identical to the original besides the following changes:

  • The MCP2515 is a TSSOP instead of an SOIC (but the wiring is changed to accommodate the different pins)
  • The Arduino (which was a Nano on breadboard) has been replaced with an ATMega2560 using MegaCore (https://github.com/MCUdude/MegaCore)
  • The ATMega2560 uses a 16MHz external crystal (as opposed to the Nano’s 8MHz)

Note that the MCP2515 is on an 8MHz external clock in both circuits. So I have the proper setting in the code to use that. Here is the test code I’m using:

#include <SPI.h>
#include <mcp2515.h>

struct can_frame canMsg;
MCP2515 mcp2515(10); // Pin 10 is used on the Nano setup, Pin 28 is the SPI CS pin on my ATmega2560-based PCB (AVR Pinout scheme)
unsigned long lastFrame = 0;

struct can_frame canMsgRx;
struct can_frame canMsgTx;

void setup() {
  Serial.begin(115200);
  SPI.begin();

  canMsgTx.can_id  = 0x0F6;
  canMsgTx.can_dlc = 8;
  canMsgTx.data[0] = 0x8E;
  canMsgTx.data[1] = 0x87;
  canMsgTx.data[2] = 0x32;
  canMsgTx.data[3] = 0xFA;
  canMsgTx.data[4] = 0x26;
  canMsgTx.data[5] = 0x8E;
  canMsgTx.data[6] = 0xBE;
  canMsgTx.data[7] = 0x86;

  mcp2515.reset();
  mcp2515.setBitrate(CAN_125KBPS, MCP_8MHZ); //Using an 8MHz external clock
  mcp2515.setNormalMode();

  Serial.println("Normal Mode");
}

void loop() {

  // Write something every 100ms
  if ( ( millis() - lastFrame ) >= 100 ) {
    mcp2515.sendMessage(&canMsgTx);
    lastFrame = millis();
  }

  // Check if there's anything to read
  if (mcp2515.readMessage(&canMsgRx) == MCP2515::ERROR_OK) {

    Serial.print(canMsgRx.can_id, HEX); // print ID
    Serial.print(" ");
    Serial.print(canMsgRx.can_dlc, HEX); // print DLC
    Serial.print(" ");

    for (int i = 0; i < canMsgRx.can_dlc; i++)  { // print the data

      Serial.print(canMsgRx.data[i], HEX);
      Serial.print(" ");

    }

    Serial.println();
  }

}

Now I don’t expect to get anything back in my test circuit (since the can bus is just hanging), but it should be sending out that specific message repeatedly. And on my test PCB, exactly that is happening. I can see the data on Pin 1 (TX data) of the 2515 passing to the 2551, which then tries to put it on the bus as CAN data.

The problem lies in my ATMega2560 circuit, which seems to be failing at the 2515 output side of things.
I can see my SPI data all coming in the same way, and I’ve probed it to ensure that it is identical, which it mostly is (more on that in a bit). I also checked every other pin across both devices, and the voltages and signals are all in-line:

Name        |  SOIC MCP2515 (Breadboard)   |  TSSOP MCP2515 (PCB)          |
            | Pin# | Saw...                | Pin# | Saw...                 |
TXCAN       |    1 | Proper TxCAN data     |    1 | 80% duty ~5kHz square? |
RXCAN       |    2 | Proper RxCAN data     |    2 | 60% duty ~5kHz square? |
CLKOUT/SOF  |    3 | Blip at data start    |    3 | Blip at data start     |
TX0RTS!     |    4 | 5V                    |    4 | 5V                     |
TX1RTS!     |    5 | 5V                    |    5 | 5V                     |
NC          |    - |                       |    6 | GND                    |
TX2RTS!     |    6 | 5V                    |    7 | 5V                     |
OSC2        |    7 | 8MHz square           |    8 | 8MHz square            |
OSC1        |    8 | 8MHz square           |    9 | 8MHz square            |
Vss         |    9 | GND                   |   10 | GND                    |
RX1BF!      |   10 | GND                   |   11 | GND                    |
RX0BF!      |   11 | GND                   |   12 | GND                    |
INT!        |   12 | GND                   |   13 | GND                    |
SCK         |   13 | Intermittent clocking |   14 | Same as BBoard         |
NC          |    - |                       |   15 | GND                    |
MOSI        |   14 | Intermittent data     |   16 | Same as BBoard         |
MISO        |   15 | 3 short pulses        |   17 | Same as BBoard         |
CS!         |   16 | 57.6kHz square        |   18 | 38.9kHz square (!!!)   |
Reset!      |   17 | 5V                    |   19 | 5V                     |
Vdd         |   18 | 5V                    |   20 | 5V                     |

Now here is the issue. The output data is messed up, and the CS pin seems to have a different speed between the Nano on breadboard (57.6kHz) and the ATMega2560 on PCB (38.9kHz). All the rest of the data on SPI looks about identical, down to the timing, so it’s strange to me that just CS would have different timings. I’m not sure what’s causing this.

Is this difference in CS pin timing a problem that could cause the corrupted output on the 2515? Or are there any other concerns I should have with my PCB application of the 2515/2551 vs. the working breadboard one?

Check the return codes from reset, setBitrate and setNormalMode.
Do a loopback test.

mikb55:
Check the return codes from reset, setBitrate and setNormalMode.

Good call. I modified this bit to print the return codes:

uint8_t ret;
  
  ret = mcp2515.reset();
  Serial.println(ret);
  ret = mcp2515.setBitrate(CAN_125KBPS, MCP_8MHZ);
  Serial.println(ret);
  ret = mcp2515.setNormalMode();
  Serial.println(ret);

And got:

0
1
0

Looking into the library, 0 is “ERROR_OK” and 1 is “ERROR_FAIL”
So then I checked setBitrate a bit more, and did some more diagnosis to find out that the function call stack right at the start of setBitrate, to “setConfigMode”, to “setMode(CANTRL_REQOP_CONFIG)”, is failing.

Here’s the final function in the stack there. So this must be returning ERROR_FAIL, it looks like due to a timeout.

MCP2515::ERROR MCP2515::setMode(const CANCTRL_REQOP_MODE mode)
{
    modifyRegister(MCP_CANCTRL, CANCTRL_REQOP, mode);

    unsigned long endTime = millis() + 10;
    bool modeMatch = false;
    while (millis() < endTime) {
        uint8_t newmode = readRegister(MCP_CANSTAT);
        newmode &= CANSTAT_OPMOD;

        modeMatch = newmode == mode;

        if (modeMatch) {
            break;
        }
    }

    return modeMatch ? ERROR_OK : ERROR_FAIL;

}

So what gives here? Perhaps this is some difference between how the Nano would handle this vs. the Mega2560?

Edit: I tested on the Nano w/Breadboard and I see no errors in the return codes with that setup, so it’s definitely a difference between the boards. Actually though, I was getting the exact same error on breadboard at first before I noticed to change my CAN CS pin to the one the Nano is wired for (D10).
Now I know the CAN CS pin is wired properly on my PCB setup to be pin 28 as I’ve beep tested it, but maybe there’s some incompatibility with using that specific pin that I’m missing? Can I just use any GPIO to accomplish the CAN CS job? I’ll try rewiring.

mikb55:
Do a loopback test.

I forgot to mention it, but loopback actually works on both setups I have, both the breadboard w/ Nano and the PCB w/ ATmega2560. So wherever the problem is, it’s past the loopback stage.

Quick update - I tried modifying the endTime declaration to be "millis() + 1000" (e.g. one second) and with that extra time allotment, now the function completes properly and I'm getting all ERROR_OK returns from the initialization functions. So maybe that problem could be chalked up to some power supply/reset difference between my PCB and the Nano - not a large issue at the moment.

...But, even with this fix made and all the initialization working, all the values I see on the ICs are still identical to what I characterized before. I'm still getting definitely-not-data on the Tx input of the MCP2551, and my CS pin is still at a different frequency. What else could be wrong here, if all the initialization succeeds?

If the loopback works as you claim it does, then the SPI communication is working.

Next step is to do a send/receive test to another CAN bus device. If that works, then you don't have a "corrupted output" problem.

I don't doubt the SPI data necessarily, as beyond the difference in the CS send frequency the signals look the same between the working and non-working boards. But what is coming immediately out of the MCP2515 after that point is markedly different between the working breadboard and the non-working PCB.

I can try to capture scope images, but basically the TXCAN pin on the working breadboard shows actual CAN data (kind of just your usual scramble of 1s and 0s packed into bytes) whereas that pin on the PCB is quizzically just a uniform pulse wave, maybe 80% on and 20% off.

So this clearly isn't valid data and no reasonable CAN device would bother interpreting it. It does make it through the MCP2551 and onto the bus, but I have a bus analyzer tool set up (Canalyzer) and it understandably classifies what is happening as a framing error.

I'll dig more into this soon and perhaps share images, but that is the current situation I'm in.