Forum Discussion
Hi Nitin123,
Thanks for reviewing the waveform and agree with your statement that the SPI transaction looks healthy.
To answer to your concerns:
- 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. - 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.
- 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.)
- 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? - 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? - LOAD TX BUFFER instruction
There is a dedicated instruction that writes to address 0x31,
Please give it a try too.
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
- Yoda_Altera8 months ago
New Contributor
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.
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.- 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:
- 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.
- nitin1237 months ago
New Contributor
Hello Yoda_Altera,
Sorry for the delayed response — I was on leave due to some personal work.
It turns out that the MCP2515 module I was using was faulty. I purchased a new one, and it’s working correctly now.
Also, thank you for pointing out the issue with the read_reg function — I’ve corrected it as per your suggestion.
Thanks a lot for your help!
and regarding this Utilizing HPS CAN Controller Without DDR i will try later.
Best regards,
Nitin