Slave Select between two SPI slaves

Hardware:
Arduino Uno
SparkFun microSD Shield (https://www.sparkfun.com/products/9802)
MicroMag Magnetometer (https://www.sparkfun.com/products/244)

Both shield and magnetometer above use SPI transmission, so I thought a differing slave select for each slave would allow communication to both in one program.

Raw Code:

  #define SCLK 7
  //#define MISO 12
  //#define MOSI 11
  #define SSMAG 4
  #define DRDY 3
  #define RESET 2
  #define SSSD 8
  #include <SoftwareSerial.h>
  #include <math.h>
  #include <SdFat.h>
  SdFat sd;
  SdFile myFile;
  int total = 1;
  int mag;
  int x = 0;
  int y = 0;
  int z = 0;
  void setup(){
    if (!sd.begin(SSSD)) sd.initErrorHalt();
    Serial.begin(9600);
    pinMode(10, OUTPUT);
    pinMode(SSMAG, OUTPUT);
    pinMode(SSSD, OUTPUT);
    pinMode(RESET, OUTPUT);
    pinMode(MOSI, OUTPUT);
    pinMode(MISO, INPUT);
    pinMode(DRDY, INPUT);
    pinMode(SCLK, OUTPUT);
    Serial.print("#\tx\ty\tz\t|B|\n");
    if (!myFile.open("mag.txt", O_WRITE | O_CREAT | O_AT_END)){
      sd.errorHalt("opening mag.txt for write failed");
    } 
    float real = mag*0.03;
    myFile.println('#\tB (uT)');
    myFile.close();
  }
  void loop(){
    Serial.print(int(total));
    Serial.print('\t');
    x=readaxis(0);
    y=readaxis(1);
    z=readaxis(2);
    getMag(x,y,z);
    writeSD(mag);
    total++;
  }
  void send_bit(int _high){
    digitalWrite(MOSI, _high);
    delayMicroseconds(300);
    digitalWrite(SCLK, HIGH);
    delayMicroseconds(300);
    digitalWrite(SCLK, LOW);
    delayMicroseconds(300);
  }
  int receive_bit(){
    digitalWrite(SCLK, HIGH);
    delayMicroseconds(300);
    int bit = digitalRead(MISO);
    delayMicroseconds(300);
    digitalWrite(SCLK, LOW);
    delayMicroseconds(300);
    return bit;
  }
  float readaxis(int _axis){  
    digitalWrite(RESET, LOW);
    delayMicroseconds(300);
    digitalWrite(RESET, HIGH);
    delayMicroseconds(300);
    digitalWrite(RESET, LOW);
    delayMicroseconds(300)  ;
    send_bit(LOW);
    send_bit(HIGH);
    send_bit(HIGH);
    send_bit(LOW);
    send_bit(LOW);
    send_bit(LOW);
    //the last two bits select the axis
    if (_axis == 0){ //x axis
      send_bit(LOW);
      send_bit(HIGH);
    }
    else if (_axis == 1){ //y axis
      send_bit(HIGH);
      send_bit(LOW);
    }
    else{ //z axis
      send_bit(HIGH);
      send_bit(HIGH);
    }
    while (digitalRead(DRDY) == LOW){
    }
    long runningtotal = 0;
    long sign = receive_bit();
    for (int i = 14; i >= 0; i = i - 1){
      long thisbit = receive_bit();
      thisbit = thisbit << i;
      runningtotal = runningtotal | thisbit;
  
    }
    if (sign == 1){
      runningtotal = runningtotal - 32768;
    }
    if (_axis == 0){
      x = runningtotal;
      Serial.print(x,DEC);
      Serial.print("\t");
    }
    else if (_axis == 1){
      y = runningtotal;
      Serial.print(y,DEC);
      Serial.print("\t");
    }
    else{
      z = runningtotal;
      Serial.print(z,DEC);
      Serial.print("\t"); 
    } 
    return runningtotal;
  }
  int getMag(float x, float y, float z){
    int perc = 0;
    mag = sqrt((x*x)+(y*y)+(z*z));
    Serial.println(int(mag));
    return mag;
  }
  int writeSD(int mag){
    digitalWrite(SSSD, LOW);
    digitalWrite(SSMAG, HIGH);
    if (!myFile.open("mag.txt", O_WRITE | O_CREAT | O_AT_END)) {
      sd.errorHalt("opening mag.txt for write failed");
    }
    float real = mag*0.03;
    myFile.print(int(total));
    myFile.print('\t');  
    myFile.println(real, DEC);
    myFile.close();
    digitalWrite(SSSD, HIGH);
    digitalWrite(SSMAG, LOW);  
  }

Functions send_bit, receive_bit & readaxis are used to get magnetometer readings and writeSD used to send data to the microSD card.

When this code is run, these are the results on the Serial, while nothing is sent to the microSD.

#  x  y  z  |B|
1

Anyone help? Much appreciated.
(Magnetometer pin layout and .ino file attached)

shield_magno2.ino (3.19 KB)

magnosch.png

int writeSD(int mag){
  digitalWrite(SSSD, LOW);
  digitalWrite(SSMAG, HIGH);
...
  digitalWrite(SSSD, HIGH);
  digitalWrite(SSMAG, LOW);  
}

You are doing things in the wrong order here. They can't both be low at once. Change to:

int writeSD(int mag){
  digitalWrite(SSMAG, HIGH);
  digitalWrite(SSSD, LOW);
...
  digitalWrite(SSSD, HIGH);
  digitalWrite(SSMAG, LOW);  
}

That's not necessarily the only problem, but that jumped out at me.

Thank you Nick, have amended this; however the problem remains.

I should add that the magnetometer parts of the code work individually (I have a separate code for the magnetometer outputting to the Serial, which works fine). It seems that adding the microSD shield is harder than I thought!

As a work-around you can bit-bang on of the devices (presumably the magnetometer).

I did a small library to assist with that here: http://www.gammon.com.au/forum/?id=10892&reply=6#reply6

Then just choose whatever pins you want for the second one and don't worry too much about turning the SD card on and off.

Apologises Nick, could you possibly explain the bit-banging?

I assume that I have to integrate my code into your sample code, I am just confused how to do so? :cold_sweat:

You seem to be bit-banging here:

  void send_bit(int _high){
    digitalWrite(MOSI, _high);
    delayMicroseconds(300);
    digitalWrite(SCLK, HIGH);
    delayMicroseconds(300);
    digitalWrite(SCLK, LOW);
    delayMicroseconds(300);
  }
  int receive_bit(){
    digitalWrite(SCLK, HIGH);
    delayMicroseconds(300);
    int bit = digitalRead(MISO);
    delayMicroseconds(300);
    digitalWrite(SCLK, LOW);
    delayMicroseconds(300);
    return bit;
  }

However sending and receiving separately like that just doesn’t work.

The example I linked above demonstrates how to do it.

Having investigated further, the issue comes at:

(95)  while (digitalRead(DRDY) == LOW){}

It gets stuck here. It seems that introducing a second SPI slave messes up the magnetometer’s outputs… :astonished:

When your circuit powers up, both SS signals will be low, so you will get bus contention. My advice:

  1. Initialize both SS lines to HIGH in setup().

  2. In writeSD(), don't touch pin SSMAG, just take SSSD low at the start and high at the end.

  3. In readAxis(), take SSMAG low at the start and high at the end.