SPI in ISR do not work

Hello,

is it true, that the SPI.transfer do not work in an ISR, because the interrupt flag at the end of transfer is not set, so it comes to an infinite loop?

Found at Arduino Leonardo.

I would use the ISR only to set a volatile flag to indicate that there is a pending SPI transfer and then exit the ISR. Then I would do the SPI transfer and clear the ISR flag...

Doc

You can use the SPI registers directly in an ISR instead of the SPI class.
Here's the code from pg 165 of the datasheet:

void SPI_MasterInit(void)
{
/* Set MOSI and SCK output, all others input */
DDR_SPI = (1<<DD_MOSI)|(1<<DD_SCK);
/* Enable SPI, Master, set clock rate fck/16 */
SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR0);
}
void SPI_MasterTransmit(char cData)
{
/* Start transmission */
SPDR = cData;
/* Wait for transmission complete */
while(!(SPSR & (1<<SPIF)))
;
}

http://www.atmel.ca/Images/Atmel-8271-8-bit-AVR-Microcontroller-ATmega48A-48PA-88A-88PA-168A-168PA-328-328P_datasheet.pdf

Yes, but the problem is, that the SPIF seems not to be set in an ISR.

To clarify: the task is to read out an external ADC via SPI in a sampling timer interrupt.

idrino:
Yes, but the problem is, that the SPIF seems not to be set in an ISR.

If true(which seems unlikely), then that's a but in the chip you're using. From pg 167 of the datasheet:
19.3.2 2. The SPIF Flag in SPSR is set, and if the SPI interrupt is enabled, and the I-bit in SREG is set, the interrupt
routine will be executed.

This question has an interesting complexity as SPI is a bus that may be several units with different clock, phase and frequency. Also each unit has its own chip select and could have one or several interrupt pins.

Now your question need to be broken down into several parts to be able to answer it. First could SPI transfer be called from an ISR (any ISR)? It could but there are a number of restrictions. First no other SPI transfer should be in progress. No device should be selected (pre SPI transfer). Second could SPI transfer be used in SPI completion? This question is more "how is SPI completion ISR used". In this case SPI transfer would be a "DMA" style operation where the ISR fetches data from a buffer and put into the SPDR register without waiting for the flag to be set. Instead the processor continues and the ISR handles the transfer block as, for instance, in TWI and UART/Serial.

The Arduino implementation of SPI is very simple and does not really support multiple devices very well. To implement a more robust scheme much more logic is needed. If you are interested in how this can be done please see Cosa/SPI. Cosa/SPI.hh at master · mikaelpatel/Cosa · GitHub. There are also Linux SPI device drivers to be studied.

The Cosa implementation handles multiple devices with ISR and both ATmega and ATtiny USI implementation. It does not implement SPI completion interrupt handler as the SPI frequency is assumed to be greater than 1 MHz and there would not be time for anything else during a block transfer. More details on forum topic Cosa: An Object-Oriented Platform for Arduino programming - Libraries - Arduino Forum

Cheers!

the SPIF seems not to be set in an ISR.

That seems unlikely. Do you have a pointer to somewhere claiming this?

Note that SPIF won't be set till "a bunch" of clocks after you initiate the transfer (depending on how it is configured.) I would think that you could fire off a single-byte transmit in an ISR, but doing the sort of multi-byte sequence usually needed to achieve a READ would probably get into being "much too slow."

SPIF should be set but the interrupt won't fire because you are inside an ISR and on the AVRs that means interrupts are disabled. You could presumably poll the bit but it's not clear why doing a transfer inside an ISR is a good idea.


Rob

One of the caveats of using SPI is that the software functions are usually non-reentrant. That is, if an SPI function is called by two different processes in an overlapping manner, there is a danger of interference. If you examine most SPI function libraries, you will see a place where it puts data on the bus, and then waits in an endless loop for a response. If another process calls the SPI function during that loop, it will "steal" the response, leaving the original instance like a jilted bride at the altar. :stuck_out_tongue: I ran into this the hard way on a work project. The system used SPI in the main loop to write data to an SD card, and in an ISR to fetch CAN data out of an MCP2515. It would mostly work, but anywhere from a few minutes to > 24 hrs. after startup, it would hang. My solution was to turn off the ISR interrupt any time I wanted to write to the SD card, and then turn it back on when I was done. Hope this makes sense. :stuck_out_tongue: