Forum Discussion

Altera_Forum's avatar
Altera_Forum
Icon for Honored Contributor rankHonored Contributor
9 years ago

MCP2515 CANbus implementation in NIOS using alt_avalon_spi_command on 10M50DA

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!

10 Replies

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    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,
    • nitin123's avatar
      nitin123
      Icon for New Contributor rankNew Contributor

      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!

      • Yoda_Altera's avatar
        Yoda_Altera
        Icon for New Contributor rankNew Contributor

        Hello nitin123,

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

  • 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

    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.

    Regards,

  • nitin123's avatar
    nitin123
    Icon for New Contributor rankNew Contributor

    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

    • Yoda_Altera's avatar
      Yoda_Altera
      Icon for New Contributor rankNew Contributor

      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?
      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?
      3. LOAD TX BUFFER instruction
        There is a dedicated instruction that writes to address 0x31,
        Please give it a try too.

      • nitin123's avatar
        nitin123
        Icon for New Contributor rankNew Contributor

        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

  • I am not able to see your example code in the attached zip file. These are the basics steps you may start your debugging. It could be the hardware setup, the fpga configuration or the software code you have.

    To interface an Altera FPGA (such as the 10M50DA from the MAX 10 series) with an MCP2515 CAN controller using the Avalon SPI interface, you will need to follow a series of steps involving both hardware and software configurations. Here's a general guideline to get you started:

    Hardware Setup

    1. Connect the MCP2515 CAN Controller to the FPGA:

      • Use SPI connections (MOSI, MISO, SCK, CS) between the FPGA and the MCP2515.
      • Ensure the CAN transceiver is connected to the MCP2515 for signal conditioning and protocol compliance.
    2. Provide Power and Ground Connections:

      • Make sure both devices are powered and grounded appropriately according to their respective datasheets.
    3. Crystal Oscillator for MCP2515:

      • The MCP2515 typically requires a crystal oscillator for timing; make sure it's connected and configured correctly.

    FPGA Configuration

    1. Design SPI Master in FPGA:

      • Use the Quartus Prime software to design an SPI master module using the Avalon MM interface.
      • Instantiate the SPI master and connect it to your design. Ensure it includes proper clocking and data rate configurations compatible with the MCP2515.
    2. Pin Assignment:

      • Assign the pins in your FPGA design correctly to match your physical connections.

    Software Implementation

    1. Using alt_avalon_spi Commands:

      • The Altera alt_avalon_spi API provides a way to handle SPI communications. Ensure you have included this API in your project.
    2. Initialize SPI Interface:

      • Initialize the SPI interface using the appropriate commands, such as setting up baud rates, SPI mode (likely Mode 0 for MCP2515), and chip select configurations.
    3. MCP2515 Configuration:

      • Write software routines to configure the MCP2515 registers. This includes setting up CAN bit timing (refer to the MCP2515 datasheet).
      • Enable necessary MCP2515 features, such as enabling interrupts if desired.
    4. Basic Commands:

      • Use alt_avalon_spi_command() to send and receive data to/from the MCP2515. You'll typically start with setting control registers and then move onto read/write operations for CAN messages.