SPI SS and more than 1 SPI peripheral

I need more then one spi slave connected to the photon.
With the arduino I choose a different pin for every peripheral, seems this is not possible with the photon.
Has anyone used more then 1 slave peripheral ?

You can implement your own functions for selecting an SPI slave using any free pin your device before you send data to the SPI. As the slave-select signal triggering does not directly have anything to do with SPI communication then there is no need to make the SPI api bigger by implementing some kind of a SS signal handling into it.

@marcus, to add to what @Melx wrote, by default both the Core and Photon will configure the SS (A2) pin as output and set it high. If you specify a different pin with SPI.begin(pin) then it will configure that pin instead. When you have multiple SPI device libraries, often they each call SPI.begin() so be aware of the default behaviour. Since the control of the slave select is done by the user app (not the hardware), I recommend configuring and setting a device’s SS pin AFTER calling SPI.begin(). :smile:

Thanks guys,
I hoped it would work somewhat like this; no problems ahead!

I need to connect two SPI devices to a Photon. The common connections for both devices are:

MOSI = A5
MISO = A3 
SCK = A3

In the code, the definitions are:

#define SS_PIN SS
#define RST_PIN D2

RST_PIN is clear.

What does SS mean in this context? How / where does this map to A2? Is there a reason for defining it as SS rather than A2?

Specifically, will this work if I want SS to be mapped to A1 instead of the default A2:

#define SS_PIN A1

Yes, the SPI SS pin (slave select, also sometimes called CS, EN, and other names) is an output from the Photon/Electron that selects which SPI device to use. By default, the primary SPI port uses A2, but you can use any pin you want. And, of course, if you have more than one SPI device, you have to use a separate pin for each SPI device.

For example, if you’re using A2 and A1 for SS pins don’t call SPI.begin(A1) for the second port. Just use pinMode(A1, OUTPUT).

This almost always works when you use more than one of the same device. When you have different devices, you will sometimes run into troubles if the devices use different clock speed, byte order, or SPI mode. Sometimes the libraries can be modified to get around this, occasionally not.

Also, there are a few SPI sensors that don’t have a SS/CC/EN pin; those devices can be the only device on the SPI bus.

I have connected a micro SD card module and a MFRC522 module over SPI to a Photon.

The problem:
Individually the SD card module and the RFID modules work perfectly! When combined, the RFID card reader stops working.

When I disconnect the MISO pin (from the SD card) from the Photon’s A4 pin, RFID starts working. As soon as the MISO pin from SD card is connected, it stops.

Note - since the SD card module works with 5V, I am using Sparkfun level shifter for each of the 4 pins connected to the micro SD module. The 5V is taken from the Vin pin of the Photon.

I am using the SDFat lib and RFID lib by pkourany from Github. The code is simply both combined with A0 (for SD ) & A1 (for RFID) pins of the Photon connected to the CS pins of each module.

Here is the code:

#include "SdFat.h"
#include "MFRC522.h"

// Pick an SPI configuration.
// See SPI configuration section below (comments are for photon).
#define SPI_CONFIGURATION 0
//------------------------------------------------------------------------------
// Setup SPI configuration.
#if SPI_CONFIGURATION == 0
// Primary SPI with DMA
// SCK => A3, MISO => A4, MOSI => A5, SS => A2 (default)
SdFat sd;
//const uint8_t chipSelect = SS; == This is the original line
const uint8_t chipSelect = A0;
#elif SPI_CONFIGURATION == 1
// Secondary SPI with DMA
// SCK => D4, MISO => D3, MOSI => D2, SS => D1
SdFat sd(1);
const uint8_t chipSelect = D1;
#elif SPI_CONFIGURATION == 2
// Primary SPI with Arduino SPI library style byte I/O.
// SCK => A3, MISO => A4, MOSI => A5, SS => A2 (default)
SdFatLibSpi sd;
const uint8_t chipSelect = SS;
#elif SPI_CONFIGURATION == 3
// Software SPI.  Use any digital pins.
// MISO => D5, MOSI => D6, SCK => D7, SS => D0
SdFatSoftSpi<D5, D6, D7> sd;
const uint8_t chipSelect = D0;
#endif  // SPI_CONFIGURATION
//------------------------------------------------------------------------------
#define SS_PIN A1
#define RST_PIN D2

// Put the device in WiFi off mode by default
	SYSTEM_MODE(SEMI_AUTOMATIC);

File myFile;

MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance.

void setup() {
  Serial.begin(9600);
  while (!Serial) {}  // wait for Leonardo

  Serial.println("Type any character to start");
  delay(5000);
  Serial.println("Starting...");
//  while (Serial.read() <= 0) {}

  // Initialize SdFat or print a detailed error message and halt
  // Use half speed like the native library.
  // Change to SPI_FULL_SPEED for more performance.
  if (!sd.begin(chipSelect, SPI_HALF_SPEED)) {
    Serial.println("Error - halting");
    sd.initErrorHalt();
  }

  // open the file for write at end like the "Native SD library"
  if (!myFile.open("test.txt", O_RDWR | O_CREAT | O_AT_END)) {
    sd.errorHalt("opening test.txt for write failed");
  }
  // if the file opened okay, write to it:
  Serial.print("Writing to test.txt...");
  myFile.println("testing 1, 2, 3.");
  myFile.printf("fileSize: %d\n", myFile.fileSize());

  // close the file:
  myFile.close();
  Serial.println("done.");

  // re-open the file for reading:
  if (!myFile.open("test.txt", O_READ)) {
    sd.errorHalt("opening test.txt for read failed");
  }
  Serial.println("test.txt content:");

  // read from the file until there's nothing else in it:
  int data;
  while ((data = myFile.read()) >= 0) {
    Serial.write(data);
  }
  // close the file:
  myFile.close();
  mfrc522.setSPIConfig();

  mfrc522.PCD_Init(); // Init MFRC522 card
  Serial.println("Scan PICC to see UID and type...");
}

void loop() {
  // Look for new cards
  if ( ! mfrc522.PICC_IsNewCardPresent()) {
    return;
  }

  // Select one of the cards
  if ( ! mfrc522.PICC_ReadCardSerial()) {
    return;
  }

  // Dump debug info about the card. PICC_HaltA() is automatically called.
  //mfrc522.PICC_DumpToSerial(&(mfrc522.uid));
  // Dump UID
  Serial.print("Card UID:");
  for (byte i = 0; i < mfrc522.uid.size; i++) {
          Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " ");
          Serial.print(mfrc522.uid.uidByte[i], HEX);
  }
  Serial.println();
  mfrc522.PICC_HaltA();
}

The way it’s supposed to work is that when the SS/CS/EN line for a SPI device is not selected (high), the SPI device should put the MISO pin in a high-impedance state, so that the other SPI slave devices on the SPI bus can write to the MISO (master in, slave out) pin. My thinking is that the level shifter has no idea it should be in Hi-Z state and just keeps driving the MISO pin, to the determent of the RFID card reader. Because most of the Photon pins, including the MISO pins A4 (and D3) are 5V tolerant, I’d try hooking up the MISO of the SD card reader directly to the Photon A4, bypassing the level shifter.
Edit: Maybe don’t do that. I’m not positive the MISO pin of the MFRC522 is 5V tolerant, even in high impedance mode. It probably is, but a safer solution would be to get a 3.3V SD card reader.

1 Like

Thanks @rickkas7. This explanation completely explains the behavior of the devices - unfortunately :frowning:

However, before I get new modules, I would risk blowing up the RFID reader and connect the pins directly :grimacing:

Interestingly in this setup the SD card module works! Is it because the card reader is in tri-state when the SD card is active but doesn’t work the other way because the SD card cannot get into tri-state.

Just tested with a 3V3 SD card module - it works!

2 Likes

Hi, I’m still not 100% getting how to configure multiple devises on SPI. Tried to switch the default PIN from the Chicpselect pin from A2 to D2. That worked, when I used SPI.begin(D2). Just calling SPI.begin() and setting the Chipselect PIN when intending to talk to the SPI device (by setting D2 LOW) did not work. I thought the concept is that there is an SPI bus. To enable that it is started by SPI.begin(). All chips are connected to the same out and inputs except the chipselect pin. By pulling all chipselect pins to high and only the one of interest to low the SPI device connect. That’s my idea of it, but certainly I’m missing something because if I program it that way it does not work. I would appreciate some more explanation.

Your general understanding of the SPI concept is correct, but you also need to consider what any potentially used libraries do and how they handle the SS pins.
Another thing is that there are different SPI modes and settings, which would also call for some context switching if “incompatible” devices want to share the same bus (see SPI.beginTransaction() )

So in order to give you any more detail, it might be good to know what devices and libraries you are using and what kind of issue you experience.

@superholz to add to what @ScruffR said, calling SPI.begin() with no argument will set the A2 (SS) pin as an output and set it HIGH. When a pin is specified the same will be done but with the specified pin.

Hi, I use four times a breakuitboard with a MAX31865 on it with each a PT100 temperature sensor attached. So the SPI mode and settings are the same for all four. For this application I do not use extra libraries. I will add an SPI display though and that might change things. I solved it so far by calling SPI.begin(userpin) for every retrieve of data from the breakout board. That works well, but still surprised that the by manually controlling chipselect pins did not work.

It should work and I’m pretty sure it does, especially since - as @peekay123 already said - SPI.begin(pin) does not do any magic to the SS pins but set them to OUTPUT and default to HIGH.
Mutual exlusivity of any active LOW state on any of the respective pins is still the responsibility of the program.

Can anyone give a bit more insight into how this relates to the SPI and SPI1 and SPI2 objects, if you need to have two SPI devices? https://docs.particle.io/reference/firmware/electron/#spi Isn’t that the easiest path? Can you use different SPI modes and clock speeds etc that way?

I’m also using the mfrc522, together with a e-ink display from Waveshare. I got both to work simultaneously by using SPI2 for all the code of the RFID reader…

You can use multiple SPI interfaces, but that requires more pins (min. 3 extra per device) and limits you to the ammount of available interfaces. While using "one" interface with multiple dedicated SS pins only requires one extra pin per device which can even be multiplexed.

Yes you can by use of SPI transactions

1 Like