SPI.transfer not being called from within library (Using Atmega32A)

Howdy! I’m currently working on a project for my Formula SAE team implementing CAN bus nodes throughout our team’s car. I have an ATmega32A MCU that I’m programming and since I have some familiarity with Arduino IDE and I like the simplicity of it, I decided to program this MCU using the SparkFun Pocket AVR programmer and a pin adaptation library.

The pin adaptation library works fine for pretty much everything, I’m able to manipulate all the digital pins, output SPI information directly from the Arduino sketch, do serial output etc. I’m using a CAN bus library from SeeedStudios since I have one of their Uno CAN shields. I know that this library works fine since it works fine with the Arduino Uno.

The problem is when I try to use the CAN bus library in sketches for the mega32. I’ve hooked up my oscilloscope and I’m not seeing any output whatsoever on the MOSI or SCK pin. The SS pin works just fine, but this is because the library modifies this pin using the digitalWrite(); command. The problem lies when this library tries to call the SPI.h library! I’ve done some debugging and it is clear that the program simply skips over the SPI.transfer commands.

void MCP_CAN::mcp2515_modifyRegister(const INT8U address, const INT8U mask, const INT8U data)
{
	Serial.print("MCP2515_SELECT \r\n");
    MCP2515_SELECT();
    spi_readwrite(MCP_BITMOD);
    spi_readwrite(address);
    spi_readwrite(mask);
    spi_readwrite(data);
    delay(10);
	Serial.print("MCP2515_UNSELECT \r\n");
    MCP2515_UNSELECT();
}

This is the first function called in the sketch that involves SPI, and when I run my program it does indeed call this function (I know this from the Serial outputs showing it runs through it).

Here are the headers for this file:

#include "mcp_can.h"
#include "pins_arduino.h"

#define spi_readwrite SPI.transfer
#define spi_read() spi_readwrite(0x00)

Does anyone have any ideas why this code wouldn’t work with my mega32? I imagine there is some problem with the fact that I’m using a programmer and a different board, but I’m not sure what to look at next. I’ll attach the hardware change files and the full CAN library .cpp, .h and _dfs.h

I’m sorry if this is too much information, but any suggestions would be appreciated! I’ll answer any questions, too.

Thanks!

mcp_can.cpp (32.3 KB)

mcp_can_dfs.h (9.96 KB)

mcp_can.h (6.03 KB)

pins_arduino.h (4.97 KB)

boards.txt (1.28 KB)

I realize I didn't post the sketch, which might be helpful. I'll just include the useful parts, since it isn't even getting out of the setup loop!

#include "SPI.h"
#include "mcp_can.h"

// v0.9b and v1.0 is default D10
const int SPI_CS_PIN = 4;

boolean ledON=1;
MCP_CAN CAN(SPI_CS_PIN);                                    // Set CS pin

void setup()
{

    Serial.begin(115200);


START_INIT:
    if(CAN_OK == CAN.begin(CAN_500KBPS))                   // init can bus : baudrate = 500k
    {
        Serial.println("CAN BUS Shield init ok!");
    }
    else
    {
        Serial.println("CAN BUS Shield init fail");
        Serial.println("Init CAN BUS Shield again");
        goto START_INIT;
    }
}

CAN.begin is below:

INT8U MCP_CAN::begin(INT8U speedset)
{
    INT8U res;

    SPI.begin();
 #if DEBUG_MODE
 Serial.print("SPIBEGIN:\r\n");
 #endif
    res = mcp2515_init(speedset);
    if (res == MCP2515_OK) return CAN_OK;
    else return CAN_FAILINIT;
}

The header file defines all the constants like CAN_OK and INT8U. The next interesting function is mcp2515_init, which is below:

INT8U MCP_CAN::mcp2515_init(const INT8U canSpeed)                       /* mcp2515init                  */
{

  INT8U res;
 
    mcp2515_reset();
    res = mcp2515_setCANCTRL_Mode(MODE_CONFIG);
    if(res > 0)
    {
#if DEBUG_MODE
      Serial.print("Enter setting mode fail\r\n"); 
#else
      delay(10);
#endif
      return res;
    }
#if DEBUG_MODE
    Serial.print("Enter setting mode success \r\n");
#else
    delay(10);
#endif

                                                                        /* set boadrate                 */
    if(mcp2515_configRate(canSpeed))
    {
#if DEBUG_MODE
      Serial.print("set rate fail!!\r\n");
#else
      delay(10);
#endif
      return res;
    }
#if DEBUG_MODE
    Serial.print("set rate success!!\r\n");
#else
    delay(10);
#endif

    if ( res == MCP2515_OK ) {

                                                                        /* init canbuffers              */
        mcp2515_initCANBuffers();

                                                                        /* 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);                                                                
        if(res)
        {
#if DEBUG_MODE        
          Serial.print("Enter Normal Mode Fall!!\r\n");
#else
            delay(10);
#endif           
          return res;
        }


#if DEBUG_MODE
          Serial.print("Enter Normal Mode Success!!\r\n");
#else
            delay(10);
#endif

    }
    return res;

}

In debug mode, I get the message "Enter setting mode fail" so I know that it is failing immediately after having the init function called. This brings me to the function call "mcp2515_setCANCTRL_Mode"

INT8U MCP_CAN::mcp2515_setCANCTRL_Mode(const INT8U newmode)
{
    INT8U i;

    mcp2515_modifyRegister(MCP_CANCTRL, MODE_MASK, newmode);
    i = mcp2515_readRegister(MCP_CANCTRL);
    i &= MODE_MASK;
    if ( i == newmode ) 
    {
        return MCP2515_OK;
    }

    return MCP2515_FAIL;

}

This one calls two more functions, but as I said before there is absolutely no SPI output form the mega32, so I feel like the real problem lies with the SPI commands. But I'll post the modifyRegister and readRegister functions anyways.

void MCP_CAN::mcp2515_modifyRegister(const INT8U address, const INT8U mask, const INT8U data)
{
    MCP2515_SELECT();
    spi_readwrite(MCP_BITMOD);   //The spi_readwrite is defined to equate to SPI.transfer
    spi_readwrite(address);
    spi_readwrite(mask);
    spi_readwrite(data);
    MCP2515_UNSELECT();
}
INT8U MCP_CAN::mcp2515_readRegister(const INT8U address)                                                                     
{
    INT8U ret;

    MCP2515_SELECT();
    spi_readwrite(MCP_READ);
    spi_readwrite(address);
    ret = spi_read();
    MCP2515_UNSELECT();

    return ret;
}

Whew! Quite a bit of code to digest just to initiate the CAN bus, but I don't think I can post too much code, because I'm sure if I want help people will really have to know what is fully going on.

Thanks again!

So I was able to actually get the program to exit the setup CAN initiation loop and actually transmit data on the MOSI pin. However, to get this to happen I had to disconnect the power and ground from my MCP2515 CAN controller. I have information being transmitted to the CAN RX pin and I verified that with the oscilloscope. It appears to be transmitting 0xC0 which form the mcp_can.h library correlates to MCP_RESET. Does anyone have any idea why removing the power from this chip caused the SPI bus to begin transmitting? It continues to do so even after plugging in the MCP2515 to power and only stops when the ATmega32 is reset.

Well, it turned out to be a damned hardware problem! I think my personal issue is that I'm not confident in my programming skills so I figured the issue was in that realm. As a lesson to everyone, if you're totally stumped, make sure to check your hardware and datasheets!

Problem 1: Oscillator output was not enabled on the MCU so the MCP2515 wasn't actually getting a clock signal.

Problem 2: The RESET on the 2515 was connected to the RESET on the Atmega32. It should really just be hooked up to a constant +5V and be reset through software.

I'm not sure if the second one was really causing my problems, but the first one clearly was.