Pages: [1]   Go Down
Author Topic: SPI Slave mode (example code)  (Read 726 times)
0 Members and 1 Guest are viewing this topic.
0
Offline Offline
Jr. Member
**
Karma: 1
Posts: 58
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
// ====================================================================
// 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()
« Last Edit: July 19, 2011, 01:23:27 pm by John Beale » Logged

nr Bundaberg, Australia
Offline Offline
Tesla Member
***
Karma: 126
Posts: 8500
Scattered showers my arse -- Noah, 2348BC.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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
 
Logged

Rob Gray aka the GRAYnomad www.robgray.com

Pages: [1]   Go Up
Jump to: