Come utilizzare la scheda SD con l’stm32 e la libreria SdFat

Spread the love

Fondamentale per la registrazione dei dati è l’interfacciamento con le schede SD. STM32 non supporta bene la libreria SD nativa, quindi useremo la libreria SdFat, che ha anche un fork Adafruit che useremo per altri scopi.

Ho to use SD Card Adapter STM32 Arduino IDE
Ho to use SD Card Adapter STM32 Arduino IDE

Protocollo

La scheda SD ha un’interfaccia host nativa oltre alla modalità SPI per la comunicazione con i dispositivi master. L’interfaccia nativa utilizza quattro linee per il trasferimento dei dati se il microcontrollore ha un modulo specifico per il controllo della scheda SD ha bisogno di una licenza separata per usarlo. Poiché SPI è un protocollo ampiamente utilizzato ed è disponibile nella maggior parte dei microcontrollori a basso costo, la modalità SPI è l’interfaccia ampiamente utilizzata nei sistemi embedded a basso costo. L’intervallo di tensione operativa dellea SD è compreso tra 2,7 V e 3,6 V e questo è indicato nel registro delle condizioni di funzionamento (OCR). Esiste una scheda SD a bassa potenza che funziona a 1,8 V ma non è così utilizzata.

Piedinatura SD

Esistono vari fattori di forma, ma la piedinatura di base è la stessa.

MMC SD miniSD microSD pins and size factor
Pin NumberPin NameIn SD ModeIn SPI Mode
1DAT2/XConnettore Linea dati 2No use
2DAT3/CSConnettore Linea dati 3Chip Select
3CMD/DIRiga di comando/rispostaData Input
4VDD/VDDAlimentazione (+3.3V)Alimentazione (+3.3V)
5CLK/SCLKClockSerial Clock
6VSS/VSSGroundGround
7DAT0/D0Connettore Linea dati 0Data Out
8DAT1/XConnettore Linea dati 1No use

Ora per prima cosa interfacceremo la scheda SD.

SD pinout

Moduli

Esistono vari moduli per interfacciare il tuo microcontrollore con il dispositivo e funziona esattamente con lo schema di connessione Arduino per l’adattatore 5v e come lo schema di connessione esp8266 per 3.3v. Quando ne acquisti uno, devi prestare attenzione alla tensione di esercizio.

Esiste qualche variante che supporta 3.3v e 5v, come quella qui linkata.

Puoi trovare il modulo della scheda SD su AliExpress

Libreria

Utilizzeremo la libreria SdFat per questo test, quella originale di greiman,

ma esiste un fork di Adafruit e per molti dei miei progetti lo uso.

Puoi installare la libreria dal gestore delle librerie di Arduino.

Cablaggio

Pinout STM32 STM32F1 STM32F103 STM32F103C8 low resolution
Pinout STM32 STM32F1 STM32F103 STM32F103C8 low resolution

Voglio mostrare una specie di cablaggio, grezzo e con un adattatore. Penso che il cablaggio grezzo possa essere utilizzato per comprendere meglio il flusso del protocollo.

STM32 STM32F401 STM32F401CCU6 pinout low resolution
STM32 STM32F401 STM32F401CCU6 pinout low resolution

Cablaggio della SPI primaria

Il cablaggio più comune consiste nell’utilizzare l’SPI primario, ma non è sempre la scelta migliore, soprattutto quando abbiamo SPI Flash o Ethernet sulla stessa interfaccia SPI.

STM32F1 (blue-pill)

Ecco lo schema di cablaggio grezzo sulla SPI primaria.

STM32F1 SD Card on SPI
STM32F1 SD Card on SPI

Probabilmente utilizziamo un adattatore per l’uso principale in questo modo.

STM32F1 SD Card adapter wiring on primary SPI
STM32F1 SD Card adapter wiring on primary SPI

STM32F4 (black-pill)

STM32F4 SD Card adapter wiring on primary SPI
STM32F4 SD Card adapter wiring on primary SPI

Cablaggio su SPI secondario

Ecco la soluzione con la SPI secondaria.

STM32F1 (blue-pill)

STM32F1 SD Card on secondary SPI
STM32F1 SD Card on secondary SPI

E qui con il relativo adattatore.

STM32F1 SD Card adapter wiring on secondary SPI
STM32F1 SD Card adapter wiring on secondary SPI

STM32F4 (black-pill)

STM32F4 SD Card adapter wiring on secondary SPI
STM32F4 SD Card adapter wiring on secondary SPI

Comandi

Non spiegherò tutti i comandi, ora ecco l’elenco standard del sistema FS.

Classe SdFat

bool begin (SdCsPin_t csPin, uint32_t maxSck)

[in]csPinSD card chip select pin.
[in]maxSckMaximum SCK frequency.

true per il successo o false per il fallimento.

bool begin (SdCsPin_t csPin=SS)

[in]csPinSD card chip select pin.

true per il successo o false per il fallimento.

bool begin (SdioConfig sdioConfig)

[in]sdioConfigSDIO configuration.

true per il successo o false per il fallimento.

bool begin (SdSpiConfig spiConfig)

[in]spiConfigSPI configuration.

true per il successo o false per il fallimento.

Classe FsVolume

uint32_t bytesPerCluster () const
bool chdir ()
bool chdir (const char *path)
bool chdir (const String &path)
void chvol ()
uint32_t clusterCount () const
uint32_t dataStartSector () const
uint8_t * end ()
bool exists (const char *path)
bool exists (const String &path)
uint32_t fatStartSector () const
uint8_t fatType () const
uint32_t freeClusterCount () const
bool isBusy ()
bool ls ()
bool ls (const char *path, uint8_t flags=0)
bool ls (print_t *pr)
bool ls (print_t *pr, const char *path, uint8_t flags)
bool ls (print_t *pr, uint8_t flags)
bool ls (uint8_t flags)
bool mkdir (const char *path, bool pFlag=true)
bool mkdir (const String &path, bool pFlag=true)
FsFile open (const char *path, oflag_t oflag=0X00)
FsFile open (const String &path, oflag_t oflag=0X00)
bool remove (const char *path)
bool remove (const String &path)
bool rename (const char *oldPath, const char *newPath)
bool rename (const String &oldPath, const String &newPath)
bool rmdir (const char *path)
bool rmdir (const String &path)
uint32_t sectorsPerCluster () const

Classe SdFile

int available () const
uint32_t available32 () const
void clearError ()
void clearWriteError ()
bool close ()
bool contiguousRange (uint32_t *bgnSector, uint32_t *endSector)
bool createContiguous (const char *path, uint32_t size)
bool createContiguous (FatFile *dirFile, const char *path, uint32_t size)
uint32_t curCluster () const
uint32_t curPosition () const
bool dirEntry (DirFat_t *dir)
uint16_t dirIndex () const
uint32_t dirSize ()
void dmpFile (print_t *pr, uint32_t pos, size_t n)
bool exists (const char *path)
void fgetpos (fspos_t *pos) const
int fgets (char *str, int num, char *delim=NULL)
uint32_t fileSize () const
uint32_t firstBlock () const
uint32_t firstSector () const
void flush ()
void fsetpos (const fspos_t *pos)
bool getAccessDate (uint16_t *pdate)
bool getAccessDateTime (uint16_t *pdate, uint16_t *ptime)
bool getCreateDateTime (uint16_t *pdate, uint16_t *ptime)
uint8_t getError () const
bool getModifyDateTime (uint16_t *pdate, uint16_t *ptime)
size_t getName (char *name, size_t size)
size_t getName7 (char *name, size_t size)
size_t getName8 (char *name, size_t size)
size_t getSFN (char *name, size_t size)
bool getWriteError () const
bool isBusy ()
bool isContiguous () const
bool isDir () const
bool isFile () const
bool isHidden () const
bool isLFN () const
bool isOpen () const
bool isReadable () const
bool isReadOnly () const
bool isRoot () const
bool isRoot32 () const
bool isRootFixed () const
bool isSubDir () const
bool isSystem () const
bool isWritable () const
bool ls (print_t *pr, uint8_t flags=0, uint8_t indent=0)
bool ls (uint8_t flags=0)
bool mkdir (FatFile *dir, const char *path, bool pFlag=true)
bool open (const char *path, oflag_t oflag=0X00)
bool open (FatFile *dirFile, const char *path, oflag_t oflag)
bool open (FatFile *dirFile, uint16_t index, oflag_t oflag)
bool open (FatVolume *vol, const char *path, oflag_t oflag)
bool openExistingSFN (const char *path)
bool openNext (FatFile *dirFile, oflag_t oflag=0X00)
bool openRoot (FatVolume *vol)
int peek ()
bool preAllocate (uint32_t length)
size_t printAccessDate (print_t *pr)
size_t printAccessDateTime (print_t *pr)
size_t printCreateDateTime (print_t *pr)
size_t printField (double value, char term, uint8_t prec=2)
size_t printField (float value, char term, uint8_t prec=2)
size_t printField (Type value, char term)
size_t printFileSize (print_t *pr)
size_t printModifyDateTime (print_t *pr)
size_t printName ()
size_t printName (print_t *pr)
size_t printName7 (print_t *pr)
size_t printName8 (print_t *pr)
size_t printSFN (print_t *pr)
int read ()
int read (void *buf, size_t count)
int8_t readDir (DirFat_t *dir)
bool remove ()
bool remove (const char *path)
bool rename (const char *newPath)
bool rename (FatFile *dirFile, const char *newPath)
void rewind ()
bool rmdir ()
bool rmRfStar ()
SdFile (const char *path, oflag_t oflag)
bool seekCur (int32_t offset)
bool seekEnd (int32_t offset=0)
bool seekSet (uint32_t pos)
bool sync ()
bool timestamp (uint8_t flags, uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second)
bool truncate ()
bool truncate (uint32_t length)
size_t write (const char *str)
size_t write (const void *buf, size_t count)
size_t write (uint8_t b)

Esempi

STM32F1 SDCard adapter on primary SPI
STM32F1 SDCard adapter on primary SPI

Informazioni SD ed elenco di una directory

Ecco un semplice schizzo che estrae le informazioni di base della SD e ottiene l’elenco dei file nella directory

/*
  SD card test for stm32 and SdFat library

  This example shows how use the utility libraries

    SD card attached to the primary SPI as follows:
		SS    = PA4;
		MOSI  = PA7;
		MISO  = PA8;
		SCK   = PA5;

    SD card attached to the secondary SPI as follows:
		SS    = PB12;
		MOSI  = PB14;
		MISO  = PB15;
		SCK   = PB13;

   by Mischianti Renzo <https://mischianti.org>
 
   https://www.mischianti.org
*/
#include <SPI.h>
//#include <SD.h>
#include "SdFat.h"

// #define SD_CS_PIN PA4

#define SD_CS_PIN PB12
static SPIClass mySPI2(PB15, PB14, PB13, SD_CS_PIN);
#define SD2_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(18), &mySPI2)

SdFat SD;

void printDirectory(File dir, int numTabs);

void setup() {
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }


  Serial.print("\nInitializing SD card...");

  // we'll use the initialization code from the utility libraries
  // since we're just testing if the card is working!
  if (!SD.begin(SD2_CONFIG)) {
  // if (!SD.begin(SD_CS_PIN)) {
    Serial.println("initialization failed. Things to check:");
    Serial.println("* is a card inserted?");
    Serial.println("* is your wiring correct?");
    Serial.println("* did you change the chipSelect pin to match your shield or module?");
    while (1);
  } else {
    Serial.println("Wiring is correct and a card is present.");
  }

  uint32_t cardSize = SD.card()->sectorCount();

  // print the type of card
  Serial.println();
  Serial.print("Card type:         ");
  switch (SD.card()->type()) {
  case SD_CARD_TYPE_SD1:
    Serial.println(F("SD1"));
    break;
  case SD_CARD_TYPE_SD2:
    Serial.println(F("SD2"));
    break;

  case SD_CARD_TYPE_SDHC:
    if (cardSize < 70000000) {
      Serial.println(F("SDHC"));
    } else {
      Serial.println(F("SDXC"));
    }
    break;

  default:
    Serial.println(F("Unknown"));
  }


//  print the type and size of the first FAT-type volume
  uint32_t volumesize;
  Serial.print("Volume type is:    FAT");
  Serial.println(int(SD.vol()->fatType()), DEC);

  Serial.print("Card size:  ");
  Serial.println((float) 0.000512*cardSize);

  Serial.print("Total bytes: ");
  Serial.println(0.000512*SD.vol()->clusterCount()*SD.sectorsPerCluster());

  Serial.print("Free bytes: ");
  Serial.println(0.000512*SD.vol()->freeClusterCount()*SD.sectorsPerCluster());

  File dir =  SD.open("/");
  printDirectory(dir, 0);

}

void loop(void) {
}

void printDirectory(File dir, int numTabs) {
  while (true) {

    File entry =  dir.openNextFile();
    if (! entry) {
      // no more files
      break;
    }
    for (uint8_t i = 0; i < numTabs; i++) {
      Serial.print('\t');
    }
    entry.printName(&Serial);

    if (entry.isDirectory()) {
      Serial.println("/");
      printDirectory(entry, numTabs + 1);
    } else {
      // files have sizes, directories do not
      Serial.print("\t\t");
      Serial.print(entry.size(), DEC);
      uint16_t pdate;
      uint16_t ptime;
      entry.getModifyDateTime(&pdate, &ptime);

      Serial.printf("\tLAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", FS_YEAR(pdate), FS_MONTH(pdate), FS_DAY(pdate), FS_HOUR(ptime), FS_MINUTE(ptime), FS_SECOND(ptime));
    }
    entry.close();
  }
}

L’uscita seriale diventa così.

Initializing SD card...Wiring is correct and a card is present.

Card type:         SDHC
Volume type is:    FAT32
Card size:  31267.49
Total bytes: 31254.90
Free bytes: 31249.83
test.txt                72      LAST WRITE: 2022-01-01 00:00:00
Bench.dat               5000000 LAST WRITE: 2022-01-01 00:00:00

Configurazione dell’interfaccia SPI primaria e secondaria

STM32F1 SD Card adapter on SPI2
STM32F1 SD Card adapter on SPI2

Ma fai attenzione alla configurazione SPI. Se desideri utilizzare l’interfaccia SPI primaria, puoi configurarla in questo modo:

#define SD_CS_PIN PA4
SdFat SD;

[...]

  if (!SD.begin(SD_CS_PIN)) {
      Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");

ma se intendi utilizzare l’interfaccia SPI secondaria, devi dichiarare una nuova SPIClass.

#define SD_CS_PIN PB12
static SPIClass mySPI2(PB15, PB14, PB13, SD_CS_PIN);
#define SD2_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(18), &mySPI2)

SdFat SD;

[...]

  if (!SD.begin(SD2_CONFIG)) {
      Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");

Ho impostato la velocità massima dell’interfaccia SPI per STM32, ma puoi usare la variabile SPI_FULL_SPEED o l’altra variabile standard.

Specifico anche che l’interfaccia SPI viene utilizzata solo per la scheda SD con la variabile DEDICATED_SPI invece di SHARED_SPI.

Esempio di lettura/scrittura

E ora il classico esempio di scrittura/lettura.

/*
  SD card read/write for stm32 and SdFat library

  This example shows how use the utility libraries

    SD card attached to the primary SPI as follows:
		SS    = PA4;
		MOSI  = PA7;
		MISO  = PA8;
		SCK   = PA5;

    SD card attached to the secondary SPI as follows:
		SS    = PB12;
		MOSI  = PB14;
		MISO  = PB15;
		SCK   = PB13;

   by Mischianti Renzo <https://mischianti.org>
 
   https://www.mischianti.org
*/

#include <SPI.h>
//#include <SD.h>
#include "SdFat.h"

#define SD_CS_PIN PA4
// #define SD_CS_PIN PB12
// static SPIClass mySPI2(PB15, PB14, PB13, SD_CS_PIN);
// #define SD2_CONFIG SdSpiConfig(PB12, DEDICATED_SPI, SPI_FULL_SPEED, &mySPI2)

SdFat SD;


File myFile;

void setup() {
  // Open serial communications and wait for port to open:
  Serial.begin(115200);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }


  Serial.print("Initializing SD card...");

  // if (!SD.begin(SD2_CONFIG)) {
  if (!SD.begin(SD_CS_PIN)) {
      Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");

  // open the file. note that only one file can be open at a time,
  // so you have to close this one before opening another.
  myFile = SD.open("test.txt", FILE_WRITE);

  // if the file opened okay, write to it:
  if (myFile) {
    Serial.print("Writing to test.txt...");
    myFile.println("testing 1, 2, 3.");
    // close the file:
    myFile.close();
    Serial.println("done.");
  } else {
    // if the file didn't open, print an error:
    Serial.println("error opening test.txt");
  }

  // re-open the file for reading:
  myFile = SD.open("test.txt");
  if (myFile) {
    Serial.println("test.txt:");

    // read from the file until there's nothing else in it:
    while (myFile.available()) {
      Serial.write(myFile.read());
    }
    // close the file:
    myFile.close();
  } else {
    // if the file didn't open, print an error:
    Serial.println("error opening test.txt");
  }
}

void loop() {
  // nothing happens after setup
}

And here is the Serial output.

Initializing SD card...initialization done.
Writing to test.txt...done.
test.txt:
testing 1, 2, 3.

Ed ecco l’uscita seriale.

Grazie

  1. How to use SD card with esp8266 and Arduino
  2. How to use SD card with esp32
  3. How to use SD card with stm32 and SdFat library

  1. STM32F1 Blue Pill: piedinatura, specifiche e configurazione IDE Arduino (STM32duino e STMicroelectronics)
  2. STM32: programmazione (STM32F1) via USB con bootloader STM32duino
  3. STM32: programmazione (STM32F1 STM32F4) tramite USB con bootloader HID
  4. STM32F4 Black Pill: pinout, specifiche e configurazione IDE Arduino
  5. STM32: ethernet w5500 standard (HTTP) e SSL (HTTPS)
  6. STM32: ethernet enc28j60 standard (HTTP) e SSL (HTTPS)
  7. STM32: WiFiNINA con un ESP32 come WiFi Co-Processor
    1. STM32F1 Blue-pill: shield WiFi (WiFiNINA)
    2. STM32F4 Black-pill: shield WiFi (WiFiNINA)
  8. Come utilizzare la scheda SD con l’stm32 e la libreria SdFat
  9. STM32: memoria flash SPI FAT FS
  10. STM32: RTC interno, sistema orario e backup batteria (VBAT)
  11. STM32 LoRa
  1. STM32 Risparmio energetico
    1. STM32F1 Blue-Pill gestione clock e frequenza
    2. STM32F4 Black-Pill gestione clock e frequenza
    3. Introduzione e framework Arduino vs STM
    4. Libreria LowPower, cablaggio e Idle (STM Sleep).
    5. Sleep, deep sleep, shutdown e consumo energetico
    6. Sveglia da allarme RTC e Seriale
    7. Sveglia da sorgente esterna
    8. Introduzione al dominio di backup e conservazione delle variabili durante il RESET
    9. Registro di backup RTC e conservazione della SRAM
  2. STM32 invia email con allegati e SSL (come Gmail): w5500, enc28j60, SD e SPI Flash

Spread the love

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *