Using two SPI devices with the same microcontroller

Hello everyone,
I have been working on a project which acquires the height with a BMP-280 (I2C communication) and the coordinates with a GPS NEO6M-V2 (UART) and, most importantly, a SD Card module and a NiceRF 2AD66 LoRa SX1276, both with SPI communication that alternatively activates to save and send the collected data, the libraries used were the LoRa.h by sandeepmistry and the SD.h library. The microcontroller is a NodeMCU ESP8266 V2.
The GPS, BMP and LoRa, worked correctly, however, when connecting the SD card module, neither the LoRa device nor the SD worked, nothing appeared in the serial monitor regarding the initialization of either of them and no information was saved or sent, only after an hour of the system on, the LoRa receiver starts getting a signal and some strange symbols (showed on the attached image).
We tried a simple SPI bus design as well as an improved one presented in this post [u]https://www.pjrc.com/better-spi-bus-design-in-3-steps/[/u] (the image of the SPI bus is attached as well), with 1 Kohm pull-up resistors connected to the CS pins, but nothing showed results.
Pinout: GPS and LoRa MISO are connected to GPIO12, MOSI to GPIO13 and SCLK to GPIO14. The LoRa CS pin is connected to GPIO15 and the SD CS pin is connected to GPIO11.
Code with the SD module:

#include <TinyGPS++.h>
#include <SoftwareSerial.h>
#include <LoRa.h>
#include <Adafruit_BMP280.h>
#include <Wire.h>
#include <SD.h>

int counter = 0;
const int LcsPin = 15;
const int LresetPin = 0;
#define frequency 915E6

#define local_pressure 1018
#define n 10

const int ScsPin = 4;

boolean eject = false; // Parachute deployment status
float height = 0,height_max = 0; 
float p_actual = 0, sum=0;
double h_base = 0;

Adafruit_BMP280 bmp; // I2C

float LAT=0,LNG=0;

static const int RXPin = 3, TXPin = 2;
static const uint32_t GPSBaud = 9600;

TinyGPSPlus gps; // The TinyGPS++ object

SoftwareSerial ss(RXPin, TXPin); // The serial connection to the GPS device

void setup()
{
  pinMode(LcsPin, OUTPUT);
  pinMode(ScsPin, OUTPUT);
  digitalWrite(LcsPin, HIGH);
  digitalWrite(ScsPin, HIGH);
  
  Serial.begin(9600);

  if (!bmp.begin(0x76)) {
    Serial.println("BMP not initialized");
    }
  bmp.setSampling(Adafruit_BMP280::MODE_NORMAL,     /* Operating Mode. */
                  Adafruit_BMP280::SAMPLING_X2,     /* Temp. oversampling */
                  Adafruit_BMP280::SAMPLING_X16,    /* Pressure oversampling */
                  Adafruit_BMP280::FILTER_X4,       /* Filtering. */
                  Adafruit_BMP280::STANDBY_MS_1);   /* Standby time. */

  for(int i = 0; i <= 9; i++) {
      sum += bmp.readAltitude(local_pressure);                   /* Calculates the 10 first measurements average to define height 0 of launch */
  }
  h_base = sum / 10;
  
  ss.begin(GPSBaud);
}

void loop()
{

  p_actual = bmp.readPressure();

  float height = float(bmp.readAltitude(local_pressure));
  String s_height = String(height,4);
  
  printInt(gps.satellites.value(), gps.satellites.isValid(), 5);
  printFloat(gps.hdop.hdop(), gps.hdop.isValid(), 6, 1);
  printFloat(gps.location.lat(), gps.location.isValid(), 11, 6);
  printFloat(gps.location.lng(), gps.location.isValid(), 12, 6);
  printInt(gps.location.age(), gps.location.isValid(), 5);
  printDateTime(gps.date, gps.time);                                   //These commands are maintaned to test out GPS' functions

  SPI.begin();
  LoRa.setPins(LcsPin, LresetPin);
  digitalWrite(ScsPin, HIGH);
  digitalWrite(LcsPin, LOW);
  LoRa.setFrequency(frequency);
  if (!LoRa.begin(frequency)) {
    Serial.println("Starting LoRa failed");
    while(1);
  }
    Serial.println("LoRa initialized");
    LoRa.beginPacket();
    float LAT = float(gps.location.lat());
    float LNG = float(gps.location.lng());
    String SLAT = String(LAT, 6);
    String SLNG = String(LNG, 6);
    LoRa.println(s_height);
    LoRa.println(SLAT);
    LoRa.println(SLNG);
    LoRa.println(counter);
    printInt(gps.charsProcessed(), true, 6);
    printInt(gps.sentencesWithFix(), true, 10);
    printInt(gps.failedChecksum(), true, 9);
    Serial.println();
    LoRa.endPacket();
    SPI.end();

  digitalWrite(LcsPin, HIGH);
  digitalWrite(ScsPin, LOW);
  
  SPI.begin();
  if (!SD.begin(ScsPin)) {
    Serial.println("Card failed, or not present");
    while (1);
  }
  Serial.println("card initialized.");
  File dataFile = SD.open("datalog.txt", FILE_WRITE);
  if (dataFile) {
    dataFile.println(s_height);
    dataFile.println(counter);
    dataFile.close();
  }
  else {
    Serial.println("error opening datalog.txt");
  }
  
  digitalWrite(ScsPin, HIGH);
  digitalWrite(LcsPin, HIGH);
  SPI.end();
  
  counter++;
  delay(250);
  smartDelay(250);

  if (millis() > 5000 && gps.charsProcessed() < 10)
    Serial.println(F("No GPS data received: check wiring"));
}

// This custom version of delay() ensures that the gps object
// is being "fed".
static void smartDelay(unsigned long ms)
{
  unsigned long start = millis();
  do 
  {
    while (ss.available())
      gps.encode(ss.read());
  } while (millis() - start < ms);
}

static void printFloat(float val, bool valid, int len, int prec)
{
  if (!valid)
  {
    while (len-- > 1)
      Serial.print('*');
    Serial.print(' ');
  }
  else
  {
    Serial.print(val, prec);
    int vi = abs((int)val);
    int flen = prec + (val < 0.0 ? 2 : 1); // . and -
    flen += vi >= 1000 ? 4 : vi >= 100 ? 3 : vi >= 10 ? 2 : 1;
    for (int i=flen; i<len; ++i)
      Serial.print(' ');
  }
  smartDelay(0);
}

static void printInt(unsigned long val, bool valid, int len)
{
  char sz[32] = "*****************";
  if (valid)
    sprintf(sz, "%ld", val);
  sz[len] = 0;
  for (int i=strlen(sz); i<len; ++i)
    sz[i] = ' ';
  if (len > 0) 
    sz[len-1] = ' ';
  Serial.print(sz);
  smartDelay(0);
}

static void printDateTime(TinyGPSDate &d, TinyGPSTime &t)
{
  if (!d.isValid())
  {
    Serial.print(F("********** "));
  }
  else
  {
    char sz[32];
    sprintf(sz, "%02d/%02d/%02d ", d.month(), d.day(), d.year());
    Serial.print(sz);
  }
  
  if (!t.isValid())
  {
    Serial.print(F("******** "));
  }
  else
  {
    char sz[32];
    sprintf(sz, "%02d:%02d:%02d ", t.hour(), t.minute(), t.second());
    Serial.print(sz);
  }
  printInt(d.age(), d.isValid(), 5);
  Serial.println();
  smartDelay(0);
}
static void printStr(const char *str, int len)
{
  int slen = strlen(str);
  for (int i=0; i<len; ++i)
    Serial.print(i<slen ? str[i] : ' ');
  smartDelay(0);
}

Any advices on using two SPI devices? What is wrong with our program? And how effectively that SPI bus tip is?

spi_diagram_good.png

spi_diagram_good.png

There is no problem mixing correctly designed SPI devices on 3.3V Arduinos. Pullups on CS lines should not be needed on correctly written software, but cause no harm if fitted.

However there is a known issue with a lot of common ‘SD Card’ modules that are designed for 3.3V\5V operation, the MISO line does not tri-state correctly, upsetting the SPI bus.

There is a simple answer for a 3.3V microcontrollers, use an SD modules that is designed for 3.3V and has no electronics on it.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.