Forum Discussion

nitin123's avatar
nitin123
Icon for New Contributor rankNew Contributor
1 year ago

mcp2515 SPI-CAN interfacing

Hello,

I'm currently working on interfacing the MCP2515 CAN controller with a Nios II processor via SPI. While I can observe SPI transactions on a logic analyzer, I'm not receiving any responses from the MCP2515.​

Has anyone successfully interfaced the MCP2515 with a Nios II processor? If so, could you share insights or code snippets that might help identify what might be going wrong in my setup?​

Any guidance or suggestions would be greatly appreciated.

Thank you!

this is my code :

/* Bit Timing for 8MHz crystal @1Mbps */
#define CNF1_VAL 0x03 // BRP=3 (TQ=1µs)
#define CNF2_VAL 0x90 // PHSEG1=3, PRSEG=1
#define CNF3_VAL 0x02 // PHSEG2=3

/* SPI Command Helper Functions */
static void write_reg(uint8_t addr, uint8_t value) {
uint8_t tx_buf[3] = {MCP_WRITE, addr, value};
alt_avalon_spi_command(SPI_BASE, SPI_SLAVE, 3, tx_buf, 0, NULL, 0);
}

static uint8_t read_reg(uint8_t addr) {
uint8_t tx_buf[3] = {MCP_READ, addr, 0xFF};
uint8_t rx_buf[3] = {0};
alt_avalon_spi_command(SPI_BASE, SPI_SLAVE, 3, tx_buf, 3, rx_buf, ALT_AVALON_SPI_COMMAND_MERGE);
return rx_buf[2];
}

/* Initialization */
int mcp2515_tx_init(void) {
DBG("Initializing MCP2515...\n");

/* 1. Software Reset */
uint8_t reset_cmd = MCP_RESET;
alt_avalon_spi_command(SPI_BASE, SPI_SLAVE, 1, &reset_cmd, 0, NULL, 0);
alt_busy_sleep(15000); // 5ms delay

/* 2. Enter Configuration Mode */
write_reg(CANCTRL, 0x80);
alt_busy_sleep(100);

/* Verify Configuration Mode */
uint8_t mode = read_reg(CANSTAT) & 0xE0;
if(mode != 0x80) {
DBG("Config Mode Fail: 0x%02X\n", mode);
return -1;
}

/* Configure TXRTSCTRL (disable RTS functionality) */
write_reg(0x0D, 0x00); // TXRTSCTRL = 0 (digital inputs)

/* 3. Set Bit Timing */
write_reg(CNF1, CNF1_VAL);
write_reg(CNF2, CNF2_VAL);
write_reg(CNF3, CNF3_VAL);

DBG("CNF1=0x%02X, CNF2=0x%02X, CNF3=0x%02X\n",
read_reg(CNF1), read_reg(CNF2), read_reg(CNF3));

/* 4. Return to Normal Mode */
write_reg(CANCTRL, 0x00);
alt_busy_sleep(100);

/* Verify Normal Mode */
mode = read_reg(CANSTAT) & 0xE0;
if(mode != 0x00) {
DBG("Normal Mode Fail: 0x%02X\n", mode);
return -1;
}

DBG("MCP2515 Ready\n");
return 0;
}

/* CAN Frame Transmission */
static void transmit_frame(uint16_t can_id, uint8_t *data, uint8_t dlc) {
/* Check if TXREQ is clear (buffer is free) */
uint8_t txb0ctrl = read_reg(0x30); // Read TXB0CTRL
if (txb0ctrl & 0x08) { // TXREQ bit is set
DBG("TXB0 busy! Aborting...\n");
return;
}
uint8_t tx_buffer[13] = {
MCP_WRITE,
0x31, // Start at TXB0SIDH
(uint8_t)(can_id >> 3), // SIDH
(uint8_t)((can_id & 0x07) << 5), // SIDL
0x00, // EID8
0x00, // EID0
(dlc & 0x0F), // DLC
// Data starts at TXB0D0 (0x36)
0x36 // TXB0D0 address (auto-increment)
};
memcpy(&tx_buffer[7], data, dlc); // Copy data bytes

// Single transaction for header + data
alt_avalon_spi_command(SPI_BASE, SPI_SLAVE, 7 + dlc, tx_buffer, 0, NULL, 0);

// Trigger transmission
uint8_t txreq_cmd[] = {MCP_WRITE, 0x30, 0x08}; // Set TXREQ
alt_avalon_spi_command(SPI_BASE, SPI_SLAVE, 3, txreq_cmd, 0, NULL, 0);
}

/* Continuous Transmission */
void mcp2515_continuous_tx(void) {
uint8_t data[8] = {0x69}; // Test data

DBG("Starting CAN Transmission\n");
while(1) {
transmit_frame(0x100, data, 1);
alt_busy_sleep(10000); // 10ms delay
}
}

int main(void) {
if(mcp2515_tx_init() != 0) return -1;
mcp2515_continuous_tx();
return 0;
}

3 Replies