Go Down

Topic: SPI Slave mode (example code) (Read 788 times) previous topic - next topic

John Beale

Jul 19, 2011, 08:16 pm Last Edit: Jul 19, 2011, 08:23 pm by John Beale Reason: 1
No doubt this has been done before, but I didn't immediately find example code. So, here's what I came up with when I needed to read some SPI data coming from an external Master-mode SPI device.  I had to read a set of two bytes as an unsigned short, which were framed by the SS/ line (Slave_Select). I found out that you cannot reliably read the SS line (Digital_10) as a digital input line while the SPI module is active, at least with the settings I used. So I turn SPI on, read two bytes, turn SPI off again immediately, and wait for the next SS falling edge. That way I ensure correct byte synchronization (in my case).

Code: [Select]

// ====================================================================
// Arduino code example for SPI Slave Mode
// Read unsigned short (two bytes) from SPI, send word to serial port
// On 16 MHz Arduino, can work at > 500 words per second
// J.Beale July 19 2011
// ====================================================================

#define SCK_PIN   13  // D13 = pin19 = PortB.5
#define MISO_PIN  12  // D12 = pin18 = PortB.4
#define MOSI_PIN  11  // D11 = pin17 = PortB.3
#define SS_PIN    10  // D10 = pin16 = PortB.2

#define UL unsigned long
#define US unsigned short

void SlaveInit(void) {
 // Set MISO output, all others input
 pinMode(SCK_PIN, INPUT);
 pinMode(MOSI_PIN, INPUT);
 pinMode(MISO_PIN, OUTPUT);  // (only if bidirectional mode needed)
 pinMode(SS_PIN, INPUT);

 /*  Setup SPI control register SPCR
 SPIE - Enables the SPI interrupt when 1
 SPE - Enables the SPI when 1
 DORD - Sends data least Significant Bit First when 1, most Significant Bit first when 0
 MSTR - Sets the Arduino in master mode when 1, slave mode when 0
 CPOL - Sets the data clock to be idle when high if set to 1, idle when low if set to 0
 CPHA - Samples data on the trailing edge of the data clock when 1, leading edge when 0
 SPR1 and SPR0 - Sets the SPI speed, 00 is fastest (4MHz) 11 is slowest (250KHz)   */
 
 // enable SPI subsystem and set correct SPI mode
 // SPCR = (1<<SPE)|(0<<DORD)|(0<<MSTR)|(0<<CPOL)|(0<<CPHA)|(0<<SPR1)|(1<<SPR0);
}

// SPI status register: SPSR
// SPI data register: SPDR

// ================================================================
// read in short as two bytes, with high-order byte coming in first
// ================================================================
unsigned short Read2Bytes(void) {
   union {
   unsigned short svar;
   byte c[2];
 } w;        // allow access to 2-byte word, or separate bytes
 
 while(!(SPSR & (1<<SPIF))) ; // SPIF bit set when 8 bits received
 w.c[1] = SPDR;               // store high-order byte
 while(!(SPSR & (1<<SPIF))) ; // SPIF bit set when 8 bits received
 w.c[0] = SPDR;               // store low-order byte
 return (w.svar); // send back unsigned short value
}

void setup() {
 Serial.begin(115200);
 SlaveInit();  // set up SPI slave mode
 delay(10);
 Serial.println("SPI port reader v0.1");
}

// ============================================================
// main loop: read in short word (2 bytes) from external SPI master
// and send value out via serial port
// On 16 MHz Arduino, works at > 500 words per second
// ============================================================
void loop() {
 unsigned short word1;
 byte flag1;

    // SS_PIN = Digital_10 = ATmega328 Pin 16 =  PORTB.2
   // Note: digitalRead() takes 4.1 microseconds
   // NOTE: SS_PIN cannot be properly read this way while SPI module is active!
   while (digitalRead(SS_PIN)==1) {} // wait until SlaveSelect goes low (active)
   
   SPCR = (1<<SPE)|(0<<DORD)|(0<<MSTR)|(0<<CPOL)|(0<<CPHA)|(0<<SPR1)|(1<<SPR0); // SPI on
   word1 = Read2Bytes();          // read unsigned short value
   SPCR = (0<<SPE)|(0<<DORD)|(0<<MSTR)|(0<<CPOL)|(0<<CPHA)|(0<<SPR1)|(1<<SPR0);  // SPI off
 
//    float seconds = millis()/1000.0;  // time stamp takes more serial time, of course
//    Serial.print(seconds,3);  
//    Serial.print(",");
   Serial.print(word1);
   Serial.println();

}  // end loop()

Graynomad

Does the master insert a small delay between the bytes? I've always found this to be required to give the slave time to grab the last byte (at high speeds anyway).

I did a version once that IIRC transferred 8 bytes in 100uS.

______
Rob
Rob Gray aka the GRAYnomad www.robgray.com

Go Up