Unexpected behavior on SPI bus chip select line

Hi folks,

I am working with a Boron on DeviceOS 4.0.1 and trying to write to µSD card over SPI. The µSD card is becoming corrupted and the main thread running on the boron seems to lock-up periodically (no logging info coming out over serial port). This doesn't happen to every device but it (mostly) reproducible in the lab once a lock-up occurs.
.
To investigate what is going on, I hooked up a Sallae Logic Pro 8 to take logic analyzer traces of the SPI bus. These traces were taken on the Boron when it is isolated on a breakout board. The firmware was only trying to write to the µSD card at a rate of 1hz using SDfat.


It appears that the chip select line (which should be low during writes to µSD card) is not staying low during the whole write. It also appears that at the beginning of chip select going low, the cs line bounces around. I've tested this firmware on deviceOS 3.1 and 1.5.2 and saw similar behavior.

Has anyone else come across this on the SPI bus? Had similar problems integrating a µSD card? Or other debug steps I should take?

Thanks!

@afry, some questions first:

  1. Are there any other devices on the SPI bus? Which SPI bus are you using - SPI or SPI1?
  2. Is the uSD on a breakout, and if so, which?
  3. Which pin are you using for CS?

Have you tried a simple SDfat library example? This may be a good place to start. I have an uSD connected to a Boron with no other SPI devices and it works flawlessly. So you may have a wiring or other issue.

4 Likes

Thanks for your response! To address your questions:

  1. I am using SPI and the µSD card breakout board is the only device on it
  2. The breakout board for the µSD is based largely off the aadafruit microSD breakout board. However, the traces I attached in the origianl post were taken on a boron that was isolated on a breadboard.
  3. For CS, I am using A5

I'll try a simple SDfat example and see if that generates any other leads.

1 Like

Hi folks,

I tested a basic firmware that writes to SD card a default value and also records logs to SD card. For logging, I am using SdCardLogHandler.

This issue with the ChipSelect line jumping high mid-write persisted through changing to SPI1 and changing the ChipSelect pin for A5. The screenshots below were taken when the Boron was isolated on a breakout board and was not connected to any peripherals.

However, when I removed the logging library, the behavior went away.

Has anyone have ideas on how/why this could be happening? The basic SD writing firmware I am using is below and I am using SdFat=1.0.16 and SdCardLogHandlerRK=0.1.0 library versions.

/*
 * Project modulair-pm
 * Description: Firmware for the QuantAQ MODULAIR(TM)-PM
 * Author: David H Hagan
 * Date: July 2020
 * 
 * Update the DeviceOS: $ particle update
 *
 * Enter DFU-Util Mode: $ stty -f /dev/tty.usbmodem1421 14400
 * Enter Listening Mode: $ stty -f /dev/tty.usbmodem1421 28800
 * 
 * We have extra EEPROM available to us as seen here:
 *      https://github.com/rickkas7/MCP79410RK
 */


#include "Particle.h"
#include "SdFat.h"
//#include "SdCardLogHandlerRK.h"

#define SD_CS A5
#define SD_PWR D9
#define WRITE_RATE (800)

unsigned long millisLastWrite, millisNow;

String filename = "TEST-FILE.csv";
bool sdCardEnabled;

// Define the different states
enum DeviceState {
  STARTUP,
  IDLE,
  WRITE_TO_SD,
};


// Set up the µSD Card
SdFat sd(&SPI);
SdFile LF;

// Set the initial state
DeviceState STATE = STARTUP;

// Setup logging functionality
static Logger appLog("app.modulair-pm");

// SdCardLogHandler<2048> logHandlerSD(sd, SD_CS, SPI_FULL_SPEED, LOG_LEVEL_WARN, {
//   { "app.modulair-pm", LOG_LEVEL_INFO },
//   { "app.data", LOG_LEVEL_INFO },
//   { "app.devicekeys", LOG_LEVEL_INFO },
//   { "app.rtc", LOG_LEVEL_INFO },
//   { "app.pubq", LOG_LEVEL_WARN },
//   { "comm.protocol", LOG_LEVEL_WARN },
//   { "app.pms5003", LOG_LEVEL_WARN },
//   { "app.opcn3", LOG_LEVEL_WARN },
//   { "system", LOG_LEVEL_WARN },
//   { "app", LOG_LEVEL_WARN },
// });

// INFO default
SerialLogHandler logHandler(LOG_LEVEL_INFO, {
  { "app.modulair-pm", LOG_LEVEL_INFO },
  { "app.data", LOG_LEVEL_TRACE },
  { "app.devicekeys", LOG_LEVEL_INFO },
  { "app.rtc", LOG_LEVEL_INFO },
  { "app.pubq", LOG_LEVEL_INFO },
  { "app.pms5003", LOG_LEVEL_INFO },
  { "app.opcn3", LOG_LEVEL_INFO }, // INFO
  { "system", LOG_LEVEL_INFO },
  { "app", LOG_LEVEL_INFO },
  { "comm.protocol", LOG_LEVEL_INFO },
  { "app.help", LOG_LEVEL_INFO },
  { "ncp.at", LOG_LEVEL_TRACE },
  { "net.pppncp", LOG_LEVEL_INFO },
  { "net.ppp.client", LOG_LEVEL_INFO },
  { "mux", LOG_LEVEL_INFO },
  { "system.nm", LOG_LEVEL_INFO },
  { "comm.dtls", LOG_LEVEL_INFO },
});


void startup() {
  RGB.mirrorTo(D7, D8, D6, false, true);
  System.enableFeature(FEATURE_RETAINED_MEMORY);
  System.enableFeature(FEATURE_RESET_INFO);
  //logHandlerSD.withDesiredFileSize(5000000).withMaxFilesToKeep(10).withNoSerialLogging();
}

STARTUP(startup());
SYSTEM_MODE(SEMI_AUTOMATIC);
SYSTEM_THREAD(ENABLED);

void handler();

void setup() 
{
  Serial.begin(9600);

  // Set the GPIO pins
  pinMode(SD_PWR, OUTPUT);

  // Turn on the µSD card
  digitalWrite(SD_PWR, HIGH);

  // Setup the log handler
  //logHandlerSD.setup();

  // Set the time format
  Time.setFormat(TIME_FORMAT_ISO8601_FULL);

  // If using an external SIM, set the keepAlive
  waitFor(Serial.available, 3000);

  // Turn on the cellular connection
  Cellular.on();
  Particle.connect();
}

// loop() runs over and over again, as quickly as it can execute.
void loop()
{

  switch(STATE)
  {
    case STARTUP:
    {
      millisLastWrite = millis();

      STATE = IDLE;
      break;
    }
    case IDLE:
    {
      millisNow = millis();

      if ((int)(millisNow - millisLastWrite) >= WRITE_RATE) 
      {
        STATE = WRITE_TO_SD;
      }

      break;
    }
    case WRITE_TO_SD:
    {
      millisLastWrite = millis();

      // Get the current time
      time_t time = Time.now();

      // Create new file if one doesn't already exist
      if (!sd.exists(filename)) {
        sdCardEnabled = makeFile(filename);

        if (!sdCardEnabled)
          appLog.error("Issue encountered while creating a new file.");
      }

      if (!LF.open(filename, O_WRONLY | O_AT_END)) {
        appLog.error("Could not open file for writing.");
        appLog.trace("Trying to restart µSD card.");

        // try starting the µSD card again
        sdCardEnabled = startSD();
      } else {
        LF.write(String::format("%d,%.1f,%.1f,%.1f\n", millis(), 1.0, 2.0, 3.0));
        LF.close();

        appLog.info("Wrote a successful string and turned SD true.");
      }

      STATE = IDLE;

      break;
    }

  } // end switch statement

  // // Update the log handler
  //logHandlerSD.loop();

}

/**
 * Start the µSD card and start the publishQueue
 */
bool startSD() {
  // Power Cycle the pins
  if (!sd.begin(SD_CS)) {
    appLog.error("Could not open the µSD card for writing");
    return false;
  }

  return true;
}

/**
 * Create a new data file in the DATA directory
 * 
 * @param fname Filename as a string
 */
bool makeFile(String fname) {
  // Make a new file if one doesn't already exist with this filename
  SdFile _LF;
  String s;

  // If the file already exists, return true
  if (!_LF.open(fname, O_WRONLY | O_CREAT | O_AT_END)) {
    return false;
  }

  // Build up a file and write
  s.concat("millis,a,b,c");
  s.concat("\n");

  _LF.write(s);
  _LF.close();

  return true;
}