By Jeff Gray, February 2008

This code, adapted from code written by John Schimmel, allows you to use SPI on any digital pins on the Arduino, and does utilize AVR SPI interrupts. These functions will work in a simple SPI communication scheme, but may not be appropriate if you are trying to utilize the SPI protocol at its fastest speeds.

If speed and efficiency is critical, you should explore the SPI examples on the Arduino website:

Arduino SPI and EEPROM

Arduino SPI and a Digital Potentiometer

Arduino Code

// define pin mapping
byte s_clk = 12;
byte s_miso = 11;
byte s_mosi = 10;
byte s_ss = 9;
byte s_drdy = 8;

// place them in an array so they can be easily shared with the functions
byte mainSensor[] = {s_clk, s_miso, s_mosi, s_ss, s_drdy};

void setup() {
  spi_init(mainSensor);

  // if you simply need to send a byte to set up the device
  // but don't need a response byte, setSensor is your friend
  setSensor(mainSensor, 0x03);

  // for debugging
  Serial.begin(38400);
  Serial.println("Starting");
}

void loop() {

  // readSensor will send a byte, and then wait for acknowledgement
  // at which point, it will make contact again and grab the byte
  byte result = readSensor(mainSensor, 0x1F);

  // output the result to the viewing computer
  Serial.print(0x1F,HEX);
  Serial.print(" <- Reg, Value -> ");
  Serial.println(result);

}

void setSensor(byte currentSensor[], byte reg){
  spi_transfer(currentSensor, reg); 
}

byte readSensor(byte currentSensor[], byte reg) {
  spi_transfer(currentSensor, reg); 
  spi_waitForReady(currentSensor[4]);
  // sent 0x00 doesn't matter, we're collecting the byte from the transfer before
  return spi_transfer(currentSensor,0x00); 
}  

void spi_init(byte spi_pins[]) {

  delay(60);
  // define pin directions
  pinMode(spi_pins[0], OUTPUT);
  pinMode(spi_pins[1],  INPUT);
  pinMode(spi_pins[2],  OUTPUT);
  pinMode(spi_pins[3],  OUTPUT);
  pinMode(spi_pins[4], INPUT);

  // initialise pins
  digitalWrite(spi_pins[0],HIGH);
  digitalWrite(spi_pins[3], HIGH); 

}

void spi_waitForReady(byte dataReady){
  // wait for the data ready pin to go high
  // telling you data is ready to be sent back
  while(!digitalRead(dataReady)){
    continue;
  } 
} 

byte spi_transfer(byte current_sensor[], byte data_out){

  byte i = 8;

  byte spi_clk = current_sensor[0]; //
  byte spi_miso = current_sensor[1];
  byte spi_mosi = current_sensor[2]; //
  byte spi_ss = current_sensor[3]; //
  byte spi_drdy = current_sensor[4];

  byte mask = 0;
  byte data_in = 0;

  digitalWrite(spi_ss,LOW); // select slave by lowering ss pin
  delayMicroseconds(75); //wait for 75 microseconds

  while(0 < i) {

    mask = 0x01 << --i; // generate bitmask for the appropriate bit MSB first

    // set out byte
    if(data_out & mask){
      digitalWrite(spi_mosi,HIGH); // send 1
    }
    else{
      digitalWrite(spi_mosi,LOW); // send 0
    }

    // lower clock pin, this tells the sensor to read the bit we just put out
    digitalWrite(spi_clk,LOW); // tick

    // give the sensor time to read the data
    delayMicroseconds(75);

    // bring clock back up
    digitalWrite(spi_clk,HIGH); // tock

    // give the sensor some time to think
    delayMicroseconds(20);

    // now read a bit coming from the sensor
    if(digitalRead(spi_miso)){
      data_in |= mask;
    }
    //  give the sensor some time to think
    delayMicroseconds(20);

  }

  delayMicroseconds(75); //  give the sensor some time to think
  digitalWrite(spi_ss,HIGH); // do acquisition burst

  return data_in;
}