hidden text to trigger early load of fonts ПродукцияПродукцияПродукцияПродукция Các sản phẩmCác sản phẩmCác sản phẩmCác sản phẩm المنتجاتالمنتجاتالمنتجاتالمنتجات מוצריםמוצריםמוצריםמוצרים
Nios® V/II Embedded Design Suite (EDS)
Support for Embedded Development Tools, Processors (SoCs and Nios® V/II processor), Embedded Development Suites (EDSs), Boot and Configuration, Operating Systems, C and C++
12705 Discussions

MCP2515 CANbus implementation in NIOS using alt_avalon_spi_command on 10M50DA

Altera_Forum
Honored Contributor II
2,014 Views

Hello, 

 

Straight off, I am a newbie to the FPGA game, but am (trying to) temporarily help fill a hole on our team. 

 

I have an Altera 10m50da, and am attempting to implement canbus communication using an mcp2515 controller through the alt_avalon_spi commands. 

 

  1. The main FPGA code was developed by someone other than me, and we have someone to support that, including the Qsys setup for the CANbus on SPI (3 Wire Serial) and CAN_interrupt on PIO (Parallel I/O). 

  2. I was given example NIOS2 code, I have the MCP2515 .c and .h files, and am able to send a CANbus message, but the message keeps repeating itself. 

  3. I cannot read CANbus messages yet. 

  4. I also need to be able to eventually read and write to the FPGA registers on the I2C Master (opencores.org). 

 

 

If anyone has some example NIOS code (reference design), I would appreciate it. 

 

My code is included in the attachments (if they attached correctly). I think some of this code was referenced from http://www.alteraforum.com/forum/showthread.php?t=44264

 

Thanks for any help!
(Virus scan in progress ...)
0 Kudos
8 Replies
Altera_Forum
Honored Contributor II
1,176 Views

Hi, I have started to work on MCP25625, which is similar to your MCP2515. Were you able to get it to work for you with nios? 

 

Thanks,
0 Kudos
nitin123
Beginner
319 Views

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!

0 Kudos
Yoda_Altera
Employee
277 Views

Hello nitin123,

Thank you for your question.  I will check with internal Altera teams, and will respond back with an answer soon. 

0 Kudos
Yoda_Altera
Employee
239 Views

Hi Nitin123,

Based on your comment, it should be safe to assume that:

  • the Nios II is online & working
  • the SPI HAL driver is present & can access the SPI Core soft IP
  • the IO is working fine (Observed SPI transaction on the bus line)

 

With that, I would suggest comparing the observed SPI transaction with the MCP2515 sample waveforms.

And check if there are any differences.

https://ww1.microchip.com/downloads/en/DeviceDoc/MCP2515-Stand-Alone-CAN-Controller-with-SPI-20001801J.pdf

 

Yoda_Altera_0-1745501503827.png

 

For example,

With this Read Instruction, failure may be caused by:

  • nCS line is not at logic ‘0’ level throughout the whole SPI transaction.
    (Especially when there are multiple SPI agents, and the SPI Core soft IP might select the wrong nCS line)
  • Instruction sent in SI line is invalid.
  • Address Bytes sent in SI line is invalid.
  • SCK clock cycles ends prematurely.  

 

Besides that, please review the following SPI Core settings in your design.

 

Altera’s SPI Core supports all SPI Modes.
The MCP2515 supports Mode 0,0 & Mode 1,1 only.

Yoda_Altera_4-1745501503839.png

 

Regards,

0 Kudos
nitin123
Beginner
200 Views

timing_diagram_mcp2515.jpg

 

 

 

Hello Sir,

Thank you for your prompt response.

This is the timing diagram from my logic analyzer, which confirms that the chip select (CS) line remains low throughout the entire SPI transaction. Each command, address, and data byte is transmitted in 8-bit segments. There's an observed delay of approximately 11.8 µs between bytes, which, according to various sources, is within normal expectations. The SPI clock operates continuously during each 8-bit transfer, and the CS line stays low for the full duration of the transaction, encompassing command, address, and data phases.

In my Qsys design, the SPI clock was initially set to 10 MHz but now it is 1 MHz : MCP2515's maximum supported frequency is 10 MHz. Additionally, SPI mode 0,0 has been selected, as supported by the MCP2515.

I have two specific concerns:

 

1. Chip Select Behavior: Initially, I used the macro:
IOWR_ALTERA_AVALON_SPI_SLAVE_SEL(SPI_BASE, SPI_SLAVE_SEL);

However, this caused the chip select line to toggle every 8 bits. To address this, I switched to using the alt_avalon_spi_command function, which maintains the chip select line low throughout the entire transaction, ensuring proper communication with the MCP2515.

2. Utilizing HPS CAN Controller Without DDR: Our custom Cyclone V SoC board lacks DDR memory, preventing the use of a full-fledged embedded Linux system. I'm considering leveraging the HPS-side CAN controller by writing a bare-metal program that operates from the internal SRAM (approximately 64 KB). Is this approach feasible given the memory constraints? If so, could you provide any sample programs or guidance on implementing this?

 

Now for mcp2515, I believe the software setup is correct, but to rule out hardware issues, I've ordered additional MCP2515. Any insights or sample code you can share would be immensely helpful.

Thank you

0 Kudos
Yoda_Altera
Employee
142 Views

Hi Nitin123,

Thanks for reviewing the waveform and agree with your statement that the SPI transaction looks healthy.

To answer to your concerns:

  1. Chip Select Behavior: 
    Yes, you are correct to apply the alt_avalon_spi_command function.
    It is recommended when you are not planning to write your own drivers.
  2. Utilizing HPS CAN Controller Without DDR:
    Sorry, but we don’t have a MCP2515 setup to run this.
    We are applying our experience of interacting SPI Host soft IP with other SPI peripherals to support your use case.
     
  3. Concern on Hardware Issue
    Agree, that is a valid perspective to take account of.
    Since the SPI transaction from Master looks valid, we should investigate from the Slave too.

 

Here are some MCP2515 traits that you might be interested:
(Note that, we strongly advice to approach Microchip on debugging MCP2515.
Our expertise is up to generic SPI transaction only, and also new to the MCP2515.)

  1. Write-Readback experiment
    Have you tried Read back the same MCP2515 0x31 address?
    Or run a simple write-readback experiment on any MCP2515 control register when under Configuration Mode?
    Yoda_Altera_0-1745805083800.png

     

  2. Configuration Mode upon power-up & Normal Mode
    Could it be possible that you need to switch to Normal Mode before using any of the TX Buffer?
    Yoda_Altera_1-1745805083802.png

     

    Yoda_Altera_2-1745805083803.png

     

  3. LOAD TX BUFFER instruction
    There is a dedicated instruction that writes to address 0x31,
    Please give it a try too.

 

Yoda_Altera_3-1745805083805.png

 

 

0 Kudos
nitin123
Beginner
130 Views

Hello Yoda_Altera,

I have already tried the write-readback experiment.
Here is the code snippet I used:

 

/* 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(5000); // 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;
}

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

return 0;
}

 

Currently, the read operation is failing every time after entering configuration mode and also in normal mode.
When I try to verify what I have written, it is not matching — even the timing registers CNF1, CNF2, and CNF3 are reading back as 0xFF.

Below are the write and read functions I created:

 

/* 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];
}

 

Could you kindly verify if my write and read operations are correct?

 

Regarding my previous doubts:

1. Manual Chip Select Control

I want to know if I can use this Macro for chip select:

IOWR_ALTERA_AVALON_SPI_SLAVE_SEL(SPI_BASE, SPI_SLAVE_SEL);

 

This is how I used it:

/* SPI Transfer with Manual Control */
static void spi_transfer(uint8_t *tx, uint8_t *rx, uint32_t len) {
IOWR_ALTERA_AVALON_SPI_SLAVE_SEL(SPI_BASE, SPI_SLAVE_SEL);

for (uint32_t i = 0; i < len; i++) {
/* Wait for TX Ready */
while (!(IORD_ALTERA_AVALON_SPI_STATUS(SPI_BASE) & ALTERA_AVALON_SPI_STATUS_TRDY_MSK));

/* Send Data */
IOWR_ALTERA_AVALON_SPI_TXDATA(SPI_BASE, tx ? tx[i] : 0xFF);

/* Wait for RX Ready */
while (!(IORD_ALTERA_AVALON_SPI_STATUS(SPI_BASE) & ALTERA_AVALON_SPI_STATUS_RRDY_MSK));

/* Capture Received Data (if rx buffer provided) */
if (rx) rx[i] = IORD_ALTERA_AVALON_SPI_RXDATA(SPI_BASE);
else (void)IORD_ALTERA_AVALON_SPI_RXDATA(SPI_BASE); /* dummy read */
}

/* Wait for Transmission Complete */
while (!(IORD_ALTERA_AVALON_SPI_STATUS(SPI_BASE) & ALTERA_AVALON_SPI_STATUS_TMT_MSK));

IOWR_ALTERA_AVALON_SPI_SLAVE_SEL(SPI_BASE, 0x00);
}

 

However, I observed that the chip select is toggling between every 8 bits.
To verify logically, I removed the chip select control from the SPI driver and instead controlled it manually using a PIO-based GPIO macro( this line  IOWR_ALTERA_AVALON_SPI_SLAVE_SEL(SPI_BASE, SPI_SLAVE_SEL); and the end line where i am writing 0x00 to spi_slave_sel i replaced these two lines with PIO_MACRO)  — pulling CS low at the start and high at the end.
In that case, the chip select stays low for the entire transaction verified in Logic analyzer but still issue is their.
Could you please clarify why this behavior is happening?

 

2. Utilizing HPS CAN Controller Without DDR

Could you also suggest a general procedure to use the HPS CAN controller with a bare-metal program ? i am not asking how to use it with mcp2515 i am seeking help only how to initialize only CAN contorller of HPS by using baremetal program and from internal RAM of HPS??

 

Thank you very much for your support!
Looking forward to your suggestions and corrections.

Best regards,
Nitin Dogra

0 Kudos
Yoda_Altera
Employee
69 Views
 

Hi Nitin123,

 

Currently, the read operation is failing every time after entering configuration mode and also in normal mode.
When I try to verify what I have written, it is not matching — even the timing registers CNF1, CNF2, and CNF3 are reading back as 0xFF.

Answer:
When you mentioned “not matching”, is it all 0xFF? Or there are other values?

  • If all read attempts returns 0xFF, it is possible the MCP2515 is not powered on.
  • If some read attempts returns valid value, then the MCP2515 is powered on.

The SPI SO line is High-Impedance (aka logic “1”). So, it is likely that the no Data Out is detected on the SO line.

 

Could you kindly verify if my write and read operations are correct?

                Answer:

                The write_reg is correct.

As for read_reg, I would imagine the following SPI transaction with your read_reg, which is different from the MCP2515 recommended waveform.

Yoda_Altera_3-1746152796392.png

 

 

I would suggest this instead:

                                static uint8_t read_reg(uint8_t addr) {

uint8_t tx_buf[2] = {MCP_READ, addr};

uint8_t rx_buf = 0;

alt_avalon_spi_command(SPI_BASE, SPI_SLAVE, 2, tx_buf, 1, &rx_buf, 0);

return rx_buf;

}

 

Quick tips :
Using the logic analyser, you can double-check the read_reg() and tinker around its argument until you get the exact same SPI transaction from MCP2515.

 

  1. Manual Chip Select Control

Could you please clarify why this behavior is happening?

The CS toggling is an expected behaviour.

You can replicate this behaviour by giving the flags argument - ALT_AVALON_SPI_COMMAND_TOGGLE_SS_N to alt_avalon_spi_command().

 

Quick tips :

If you are writing custom SPI driver, perhaps you can take reference from the alt_avalon_spi_command().

For example, about how to not toggle CS:

Yoda_Altera_4-1746152796392.png

 

 

 

  1. Utilizing HPS CAN Controller Without DDR

As a general procedure, I would suggest taking a known good working Uboot implementation that contains CAN, and strip it down to just only the CAN portion.  i.e. take out everything including any DDR setup.  It might fit within the HPS OCRAM.

 

 

0 Kudos
Reply