Hard fault after reinserting an SD card

Hi,

I am building a prototype that uses an uSD card to log data with Mbed OS 6.6. The user will need to be able to remove the SD card and re-insert it without physically resetting the device.
Based on this example for testing i came up with the code posted below.

Basically it mounts/unmounts every 10 seconds. Works fine until i remove/reinsert the SD card. However after doing so, it throws a hard fault next time it tries to mount even if the card is already reinserted at that time. Interestingly the erase() works even after reinserting, but only until the code tries to mount the FS.

I am kinda lost here and need help how to get it working properly. I am also wondering if this might be a bug? I mean it is the nature of SD cards that we can insert/remove them. So i would expect no probems with fs.mount() if i already made fs.unmount() before the removal.

#include “mbed.h”
#include
#include <errno.h>
#include
#include <stdio.h>

#include “SDBlockDevice.h”

// Maximum number of elements in buffer
#define BUFFER_MAX_LEN 10
#define FORCE_REFORMAT false

// This will take the system’s default block device

SDBlockDevice *blockdevice =
new SDBlockDevice(MBED_CONF_SD_SPI_MOSI, MBED_CONF_SD_SPI_MISO,
MBED_CONF_SD_SPI_CLK, MBED_CONF_SD_SPI_CS);

// Instead of the default block device, you can define your own block device.
// For example: HeapBlockDevice with size of 2048 bytes, read size 1, write size
// 1 and erase size 512. #include “HeapBlockDevice.h” BlockDevice *blockdevice = new
// HeapBlockDevice(2048, 1, 1, 512);

#include "FATFileSystem.h"
FATFileSystem filesystem("fs");

// Set up the button to trigger an erase
InterruptIn irq(BUTTON1);
DigitalOut rLED(LED_RED, LED_OFF);
int err = 0;
bool toggle = 0;
FILE *f;
void flipToggle() {
  toggle = !toggle;
  rLED = !rLED;
}
void erase() {
  printf("Initializing the block device... ");
  fflush(stdout);
  int err = blockdevice->init();
  printf("%s\n", (err ? "Fail :(" : "OK"));
  if (err) {
    error("error: %s (%d)\n", strerror(-err), err);
  }

  printf("Erasing the block device... ");
  fflush(stdout);
  err = blockdevice->erase(0, blockdevice->size());
  printf("%s\n", (err ? "Fail :(" : "OK"));
  if (err) {
    error("error: %s (%d)\n", strerror(-err), err);
  }

  printf("Deinitializing the block device... ");
  fflush(stdout);
  err = blockdevice->deinit();
  printf("%s\n", (err ? "Fail :(" : "OK"));
  if (err) {
    error("error: %s (%d)\n", strerror(-err), err);
  }
}

void mountFS() {
  printf("Mounting the filesystem... ");
  fflush(stdout);
  err = filesystem.mount(blockdevice);
  printf("%s\n", (err ? "Fail :(" : "OK"));
  if (err || FORCE_REFORMAT) {
    // Reformat if we can't mount the filesystem
    printf("formatting... ");
    fflush(stdout);
    err = filesystem.reformat(blockdevice);
    printf("%s\n", (err ? "Fail :(" : "OK"));
    if (err) {
      error("error: %s (%d)\n", strerror(-err), err);
    }
  }
}

void openFile() {
  printf("Opening \"/fs/numbers.txt\"... ");
  fflush(stdout);
  f = fopen("/fs/numbers.txt", "r+");
  printf("%s\n", (!f ? "Fail :(" : "OK"));
  if (!f) {
    // Create the numbers file if it doesn't exist
    printf("No file found, creating a new file... ");
    fflush(stdout);
    f = fopen("/fs/numbers.txt", "w+");
    printf("%s\n", (!f ? "Fail :(" : "OK"));
    if (!f) {
      error("error: %s (%d)\n", strerror(errno), -errno);
    }

    for (int i = 0; i < 10; i++) {
      printf("\rWriting numbers (%d/%d)... ", i, 10);
      fflush(stdout);
      err = fprintf(f, "    %d\n", i);
      if (err < 0) {
        printf("Fail :(\n");
        error("error: %s (%d)\n", strerror(errno), -errno);
      }
    }
    printf("\rWriting numbers (%d/%d)... OK\n", 10, 10);

    printf("Seeking file... ");
    fflush(stdout);
    err = fseek(f, 0, SEEK_SET);
    printf("%s\n", (err < 0 ? "Fail :(" : "OK"));
    if (err < 0) {
      error("error: %s (%d)\n", strerror(errno), -errno);
    }
  }
}

void incrementNumbers() {
  for (int i = 0; i < 10; i++) {
    printf("\rIncrementing numbers (%d/%d)... ", i, 10);
    fflush(stdout);

    // Get current stream position
    long pos = ftell(f);

    // Parse out the number and increment
    char buf[BUFFER_MAX_LEN];
    if (!fgets(buf, BUFFER_MAX_LEN, f)) {
      error("error: %s (%d)\n", strerror(errno), -errno);
    }
    char *endptr;
    int32_t number = strtol(buf, &endptr, 10);
    if ((errno == ERANGE) ||         // The number is too small/large
        (endptr == buf) ||           // No character was read
        (*endptr && *endptr != '\n') // The whole input was not converted
    ) {
      continue;
    }
    number += 1;

    // Seek to beginning of number
    fseek(f, pos, SEEK_SET);

    // Store number
    fprintf(f, "    %d\n", number);

    // Flush between write and read on same file
    fflush(f);
  }
  printf("\rIncrementing numbers (%d/%d)... OK\n", 10, 10);
}

void closeFile() {
  printf("Closing \"/fs/numbers.txt\"... ");
  fflush(stdout);
  err = fclose(f);
  printf("%s\n", (err < 0 ? "Fail :(" : "OK"));
  if (err < 0) {
    error("error: %s (%d)\n", strerror(errno), -errno);
  }
}

void displayDir() {
  printf("Opening the root directory... ");
  fflush(stdout);
  DIR *d = opendir("/fs/");
  printf("%s\n", (!d ? "Fail :(" : "OK"));
  if (!d) {
    error("error: %s (%d)\n", strerror(errno), -errno);
  }

  printf("root directory:\n");
  while (true) {
    struct dirent *e = readdir(d);
    if (!e) {
      break;
    }

    printf("    %s\n", e->d_name);
  }

  printf("Closing the root directory... ");
  fflush(stdout);
  err = closedir(d);
  printf("%s\n", (err < 0 ? "Fail :(" : "OK"));
  if (err < 0) {
    error("error: %s (%d)\n", strerror(errno), -errno);
  }
}

void displayFContent() {
  printf("Opening \"/fs/numbers.txt\"... ");
  fflush(stdout);
  f = fopen("/fs/numbers.txt", "r");
  printf("%s\n", (!f ? "Fail :(" : "OK"));
  if (!f) {
    error("error: %s (%d)\n", strerror(errno), -errno);
  }

  printf("numbers:\n");
  while (!feof(f)) {
    int c = fgetc(f);
    printf("%c", c);
  }

  printf("\rClosing \"/fs/numbers.txt\"... ");
  fflush(stdout);
  err = fclose(f);
  printf("%s\n", (err < 0 ? "Fail :(" : "OK"));
  if (err < 0) {
    error("error: %s (%d)\n", strerror(errno), -errno);
  }
}

void unmountFS() {
  printf("Unmounting... ");
  fflush(stdout);
  err = filesystem.unmount();
  printf("%s\n", (err < 0 ? "Fail :(" : "OK"));
  if (err < 0) {
    error("error: %s (%d)\n", strerror(-err), err);
  }
}

static auto erase_event = mbed_event_queue()->make_user_allocated_event(erase);
// static auto erase_event = mbed_event_queue() ->
// make_user_allocated_event(flipToggle);

// Entry point for the example
int main() {
  printf("--- Mbed OS filesystem example ---\n");

  // Setup the erase event on button press, use the event queue
  // to avoid running in interrupt context
  irq.fall(std::ref(erase_event));

  // Try to mount the filesystem
  mountFS();

  // Open the numbers file
  openFile();

  // Go through and increment the numbers
  incrementNumbers();

  // Close the file which also flushes any cached writes
  closeFile();

  // Display the root directory
  displayDir();

  // Display the numbers file
  displayFContent();

  // Tidy up
  unmountFS();

  printf("Mbed OS filesystem example done!\n");

  ThisThread::sleep_for(10000ms);
  while (1) {
    printf("Toggle state: %d\n", toggle);

    mountFS();
    
    unmountFS();
    ThisThread::sleep_for(10000ms);
  }
}

After a day of googling turns out many people have the same problem on different platforms. Read many threads, but only 1 gave some useful hint. We also need to deinitialize the blockdevice after unmounting the filesystem by adding
blockdevice->deinit();
at the end of unmountFS(),

After that the card reinsertion does not break the code posted above.

1 Like