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;
}