Arduino DUE SPI Interrupt

Hello everyone,

Below is a sketch that include a SPI Interrupt Routine developed by Nick Gammon for Arduino AVR boards (SPI in Slave Mode). It is extremely useful to receive data thru SPI from a microprocessor, like Raspberry Pi.
In my project I have to use a powerful microcontroller, like Atmel SAM3X. For this reason I need help to convert this sketch for Arduino DUE, but I have no idea how to do it.
I really appreciate any help!

#include <SPI.h>

char buf [100];
volatile byte pos;
volatile boolean process_it;

void setup (void)
{
 Serial.begin (115200);   // debugging

 // have to send on master in, *slave out*
 pinMode(MISO, OUTPUT);

   // turn on SPI in slave mode
 SPCR |= _BV(SPE);

 // get ready for an interrupt 
 pos = 0;   // buffer empty
 process_it = false;

 // now turn on interrupts
 SPI.attachInterrupt();

}  // end of setup

// SPI interrupt routine
ISR (SPI_STC_vect)
{
byte c = SPDR;  // grab byte from SPI Data Register

// add the grabbed byte to the buffer if enough room & increase the pos with a unit
 if (pos < sizeof buf)
   {
   buf [pos++] = c;

   //newline means it is the time to process the buffer
   if (c == '\n')

     process_it = true;

   }  // end of room available
}      // end of interrupt routine SPI_STC_vect

// main loop - wait for flag set in interrupt routine

void loop (void)
{
 if (process_it == true)
   {
   buf [pos] = 0;         // curent byte of the buffer (last one) is null

   Serial.println (buf);  // send the buffer value (string character) to Serial/USB

   pos = 0;               // set the current position "pos" to zero

   process_it = false;
    
   }  // end of flag set
}  // end of loop

AFAIK, SPI library is only written for a DUE in Master mode, hence you will have to write a code for a DUE in SPI Slave mode. On the other hand, interrupt or polling are 2 possibilities to read the data stream.

What do you know about the SPI protocol ?

Thank you for feedback.
About the SPI protocol I know only general things. I’m not a genuine programmer. That’s why I’m asking for professional help …

With the below code for Arduino DUE (a combination between Nick Gammon SPI Interrupt Routine and some codes found on internet), I succeeded to read the incoming bytes thru SPI. All the characters are red inside the void loop. As long as the void loop is kept simple and running fast there is no problem. Any attempt to increase the complexity of the void loop completes with the failure in reading the SPI incoming bytes. Definetly, the solution is to implement a SPI interrupt routine, that triggers as soon as SPI data arrived. Below is the working code, with a void loop as simple as possible (looks like the simple void loop behaves like an interrupt in this case):

char buf [100];                                 
volatile byte pos;                            
volatile boolean process_it; 

void setup() 
{
 Serial.begin(9600);
 
 //SPI serial receive 
 REG_PMC_PCER0 |= PMC_PCER0_PID24;      // Power up SPI clock
 REG_SPI0_WPMR = 0<<SPI_WPMR_WPEN;   // Unlock user interface for SPI
 
 //Instance SPI0, MISO: PA25, (MISO), MOSI: PA26, (MOSI), SCLK: PA27, (SCLK), NSS: PA28, (NPCS0) 
 REG_PIOA_ABSR |= PIO_ABSR_P25;   // Transfer Pin control from PIO to SPI
 REG_PIOA_PDR |= PIO_PDR_P25;       // Set MISO pin to an output

 REG_PIOA_ABSR |= PIO_ABSR_P26;     // Transfer Pin control from PIO to SPI
 REG_PIOA_PDR |= 0<<PIO_PDR_P26;   // Set MOSI pin to an input

 REG_PIOA_ABSR |= PIO_ABSR_P27;     // Transfer Pin control from PIO to SPI
 REG_PIOA_PDR |= 0<<PIO_PDR_P27;   // Set SCLK pin to an input

 REG_PIOA_ABSR |= PIO_ABSR_P28;     // Transfer Pin control from PIO to SPI
 REG_PIOA_PDR |= 0<<PIO_PDR_P28;   // Set NSS pin to an input

 //REG_ISER0 = 1<<24;      // Enable interrupt controller
 REG_SPI0_CR = 1;             // Enable SPI
 REG_SPI0_MR = 0;            // Slave mode 
  
//Enable interrupts  
REG_SPI0_IER = 1<<SPI_IER_RDRF|1<<SPI_IER_OVRES|1<<SPI_IER_NSSR;
// Shift on falling edge and transfer 8 bits.
SPI0->SPI_CSR[0] = SPI_CSR_NCPHA|SPI_CSR_BITS_8_BIT;   
 pos = 0;                 // get ready for an interrupt, buffer empty
 process_it = false;
}

void loop() 
{
   if(REG_SPI0_SR&0x0001 == 1)
 {
   byte c = REG_SPI0_RDR;
    if (pos < sizeof buf)
   {
   buf [pos++] = c;   // add the grabbed byte to the buffer if enough room & increase the pos with a unit
   if (c == '\n')
     process_it = true;   // newline means it is the time to process the buffer
   }                             // end of room available
}                                 // end of interrupt routine SPI
   if (process_it == true)
   {
   buf [pos] = 0;                       // curent byte of the buffer (last one) is null
   pos = 0;                               // set the current position "pos" to zero
   process_it = false;
   Serial.println(buf);
    }
}

Post your code between code tags (</>).

Some simplifications could be done in your code. Anyway, since you want to trigger an interrupt in the SPI Slave upon receiving data, this could be done this way:

void setup() {
  //.........
  // Receive Data Register Full Interrupt
  SPI0->SPI_IER = SPI_IER_RDRF;
  NVIC_EnableIRQ(SPI0_IRQn);
}


void loop() {
  //...........
}

void SPI0_Handler() {
  uint16_t DataReceived;
  uint32_t status = SPI0->SPI_SR;

  //if (status & SPI_SR_RDRF) {
    DataReceived = SPI0->SPI_RDR & SPI_RDR_RD_Msk;
  //}
  // Send something to receive something
  // if (status & SPI_SR_TDRE) {
  SPI0->SPI_TDR = (uint16_t)DataReceived;
  //  }
}

BTW, whenever you Serial Print, use Serial.begin(250000); rather than the slow 9600 baud.

Thank you very much “ard_newbie”. Your code is working very nice. I really appreciate your help.
Below is the full working code containing your modifications and comments. If you have time, please review it and refine it.

char buf [100];                                // for SPI Communication (including SPI interrupt routine)
volatile byte pos;                              // for SPI Communication (including SPI interrupt routine)
volatile boolean process_it; 
void setup() {
  Serial.begin(250000);

//SPI serial recieve 
REG_PMC_PCER0 |= PMC_PCER0_PID24;      // Power up SPI clock
REG_SPI0_WPMR = 0<<SPI_WPMR_WPEN;   //Unlock user interface for SPI

//Instance SPI0, MISO: PA25, (MISO), MOSI: PA26, (MOSI), SCLK: PA27, (SCLK), NSS: PA28, (NPCS0) 
REG_PIOA_ABSR |= PIO_ABSR_P25;    // Transfer Pin control from PIO to SPI
REG_PIOA_PDR |= PIO_PDR_P25;        // Set MISO pin to an output

REG_PIOA_ABSR |= PIO_ABSR_P26;    // Transfer Pin control from PIO to SPI
REG_PIOA_PDR |= 0<<PIO_PDR_P26;  // Set MOSI pin to an input

REG_PIOA_ABSR |= PIO_ABSR_P27;     // Transfer Pin control from PIO to SPI
REG_PIOA_PDR |= 0<<PIO_PDR_P27;   // Set SCLK pin to an input

REG_PIOA_ABSR |= PIO_ABSR_P28;     // Transfer Pin control from PIO to SPI
REG_PIOA_PDR |= 0<<PIO_PDR_P28;   // Set NSS pin to an input

REG_SPI0_CR = 1;   // Enable SPI
REG_SPI0_MR = 0;   // Slave mode 
// Receive Data Register Full Interrupt
SPI0->SPI_IER = SPI_IER_RDRF;
NVIC_EnableIRQ(SPI0_IRQn);

SPI0->SPI_CSR[0] = SPI_CSR_NCPHA|SPI_CSR_BITS_8_BIT;   // Shift on falling edge and transfer 8 bits.
pos = 0;                      // get ready for an interrupt, buffer empty
process_it = false;
}

void loop() 
{
   if (process_it == true)
  {
  buf [pos] = 0;                            // current byte of the buffer (last one) is null
  pos = 0;                                    // set the current position "pos" to zero
  process_it = false;
  Serial.println(buf);
  delay(5000);
}
}

void SPI0_Handler() 
{
//  uint16_t DataReceived;
//  uint32_t status = SPI0->SPI_SR;

// if (status & SPI_SR_RDRF) {
// DataReceived = SPI0->SPI_RDR & SPI_RDR_RD_Msk;
   byte c = SPI0->SPI_RDR & SPI_RDR_RD_Msk;
  
      if (pos < sizeof buf)
  {
  buf [pos++] = c;       // add the grabbed byte to the buffer if enough room & increase the pos with a unit
  if (c == '\n')
    process_it = true;   // newline means it is the time to process the buffer
  }                             // end of room available
                      
//}
// Send something to receive something
// if (status & SPI_SR_TDRE) {
//SPI0->SPI_TDR = (uint16_t)DataReceived;
// }
}

Post your code between code tags (</>).

Here you can find out how to use the code tags: http://forum.arduino.cc/index.php/topic,148850.0.html

I try arduino due(slave) spi to communicate with rappberry(master). The rappberry just receive 0, and the arduino due can succeed raspberry data. I test MOSI to connect MISO. The rappberry is normal, but arduino due cannot get correct data.
I check datasheet. Is there any register (PIOA_ABSR and PIOA_PDR) setting error? I modified the code and it can work as expected. the code as follows:

void setup() {
	Serial.begin(115200);
	while(!Serial);
	
	//SPI serial recieve
	REG_PMC_PCER0 |= PMC_PCER0_PID24;		// Power up SPI clock
	REG_SPI0_WPMR = 0<<SPI_WPMR_WPEN;		//Unlock user interface for SPI
	
	//Instance SPI0, MISO: PA25, (MISO), MOSI: PA26, (MOSI), SCLK: PA27, (SCLK), NSS: PA28, (NPCS0)
	REG_PIOA_ABSR &= ~PIO_ABSR_P25; 		// Transfer Pin control from PIO to SPI
	REG_PIOA_PDR |= PIO_PDR_P25;			// disable pio to control this pin (MOSI)
	
	REG_PIOA_ABSR &= ~PIO_ABSR_P26;			// Transfer Pin control from PIO to SPI
	REG_PIOA_PDR |= PIO_PDR_P26;			// disable pio to control this pin (MOSI)
	
	REG_PIOA_ABSR &= ~PIO_ABSR_P27;			// Transfer Pin control from PIO to SPI
	REG_PIOA_PDR |= PIO_PDR_P27;			// disable pio to control this pin (SCLK)
	
	REG_PIOA_ABSR &= ~PIO_ABSR_P28;			// Transfer Pin control from PIO to SPI
	REG_PIOA_PDR |= PIO_PDR_P28;			//disable pio to control this pin (NSS)
	
	REG_SPI0_CR = 1;						// Enable SPI
	REG_SPI0_MR = 0;						// Slave mode
	
	SPI0->SPI_IER = SPI_IER_RDRF;			// Receive Data Register Full Interrupt
	NVIC_EnableIRQ(SPI0_IRQn);
	
	SPI0->SPI_CSR[0] = SPI_CSR_NCPHA|SPI_CSR_BITS_8_BIT;	// Shift on falling edge and transfer 8 bits.
	Serial.println("START");
}

bool flag=false;
uint16_t DataReceived;
void SPI0_Handler()
{
	uint32_t status = SPI0->SPI_SR;
	if (status & SPI_SR_RDRF){
		DataReceived = SPI0->SPI_RDR & SPI_RDR_RD_Msk;
		flag=true;
	}
	if (status & SPI_SR_TDRE) {
		SPI0->SPI_TDR = (uint16_t)DataReceived;
	}
}

void loop() {
	if(flag){
		flag=false;
		Serial.println(DataReceived);
	}
}

If there are any errors please let me know.