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.
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);
}
}
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:
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;
// }
}
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);
}
}