Go Down

Topic: SPI Slave mode (example code) (Read 1 time) 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() {
 SlaveInit();  // set up SPI slave mode
 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(",");

}  // end loop()


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 Gray aka the GRAYnomad www.robgray.com

Go Up

Please enter a valid email to subscribe

Confirm your email address

We need to confirm your email address.
To complete the subscription, please click the link in the email we just sent you.

Thank you for subscribing!

via Egeo 16
Torino, 10131