Raspberry Pi Pico e schede rp2040: come utilizzare una scheda SD – 5

Spread the love

Abbiamo già parlato delle memorie flash interne per le schede rp2040 e gli altri microcontrollori, molto utili per archiviare pagine o file statici. Tuttavia, non adatte per la registrazione dei logs o applicazioni simili, quindi impareremo come collegare e utilizzare una scheda SD che può avere molto spazio, con fattore di forma molto piccolo e con consumi ridotti.

Raspberry Pi Pico W rp2040 how to use SD card
Raspberry Pi Pico W rp2040 how to use SD card

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, in cui il microcontrollore dispone di un modulo controller della scheda SD e necessita di una licenza separata per utilizzarlo. Poiché lo 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 di lavoro delle SD è compreso tra 2,7 V e 3,6 V, e questo è indicato nel registro delle condizioni operative (OCR). Esiste una scheda SD a basso consumo che funziona a 1.8V ma non è così utilizzata.

Piedinatura

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/XConnector Data line 2No use
2DAT3/CSConnector Data line 3Chip Select
3CMD/DICommand / Response LineData Input
4VDD/VDDPower supply (+3.3V)Power supply (+3.3V)
5CLK/SCLKClockSerial Clock
6VSS/VSSGroundGround
7DAT0/D0Connector Data line 0Data Out
8DAT1/XConnector Data line 1No use

Ora interfacciamo la scheda SD.

SD pinout

Connessione

Raspberry Pi Pico rp2040 pinout low resolution
Raspberry Pi Pico rp2040 pinout low resolution

La tensione di funzionamento ci obbliga a realizzare 2 schemi di connessione distinti in base al tipo nm di microcontrollore. Per interfacciare la scheda SD, utilizzo un adattatore SD con micro SD, il che risulta.

Vista frontale dei pins dell’adattatore SD
SD adapter pins back

Raspberry Pi Pico e scheda SD grezza

Qui il semplice schema di cablaggio sulla SPI primaria.

Raspberry Pi Pico rp2040 SD Card SPI
Raspberry Pi Pico rp2040 SD Card SPI
SD CardRaspberry Pi Pico (SPI)
CSGPIO17
SCKGPIO18
MISOGPIO16
MOSIGPIO19
GNDGND
3.3v3.3v

E qui con l’interfaccia SPI secondaria SPI1

Raspberry Pi Pico rp2040 SD Card on secondary SPI1
Raspberry Pi Pico rp2040 SD Card on secondary SPI1
SD CardRaspberry Pi Pico (SPI1)
CSGPIO13
SCKGPIO14
MISOGPIO12
MOSIGPIO15
GNDGND
3.3v3.3v

Moduli

Vari moduli interfacciano il tuo microcontrollore con il tuo dispositivo e funzionano esattamente come lo schema di connessione Arduino per l’adattatore 5v e lo schema di connessione esp8266 per 3.3v. Quando ne acquisti uno, devi prestare attenzione alla tensione di lavoro.

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

Puoi trovare il modulo della scheda SD su AliExpress

Raspberry Pi Pico e modulo scheda SD

Qui il cablaggio con SPI standard

Raspberry Pi Pico rp2040 SD Card module SPI
Raspberry Pi Pico rp2040 SD Card module SPI
SD CardRaspberry Pi Pico (SPI)
CSGPIO17
SCKGPIO18
MISOGPIO16
MOSIGPIO19
GNDGND
3.3v3.3v

e qui con l’interfaccia SPI secondaria (SPI1).

Raspberry Pi Pico rp2040 SD Card module on secondary SPI1
Raspberry Pi Pico rp2040 SD Card module on secondary SPI1
SD CardRaspberry Pi Pico (SPI1)
CSGPIO13
SCKGPIO14
MISOGPIO12
MOSIGPIO15
GNDGND
3.3v3.3v

Comandi

sd.begin() sd.begin(cspin)

Inizializza la libreria e la scheda SD. Questo avvia l’uso del bus SPI e del pin di selezione del chip, che per impostazione predefinita è il pin SS hardware. Restituisce true in caso di successo; falso in caso di fallimento.

sd.exists(filename)
Verifica se esiste un file o una directory sulla scheda SD. Restituisce vero se il file o la directory esiste, falso in caso contrario.

sd.mkdir(filename)
Crea una directory sulla scheda SD. Questo creerà anche tutte le directory intermedie che non esistono già; ad esempio SD.mkdir(“a/b/c”) creerà a, b e c. Restituisce vero se la creazione della directory è riuscita, falso in caso contrario.

sd.open(filepath) sd.open(filepath, mode)
Apre un file sulla scheda SD. Se il file viene aperto in scrittura, verrà creato se non esiste già (ma la directory che lo contiene deve già esistere). Modalità parametro ( opzionale ): la modalità in cui aprire il file è predefinita FILE_READ –  byte . uno tra FILE_READ: apre il file per la lettura, partendo dall’inizio del file. FILE_WRITE: apre il file in lettura e scrittura, partendo dalla fine del file. Restituisce un oggetto File riferito al file aperto; se il file non può essere aperto, questo oggetto risulterà falso in un contesto booleano, cioè puoi testare il valore restituito con “if (f)”.

sd.remove(filename)
Rimuove un file dalla scheda SD. Restituisce vero se la rimozione del file è riuscita, falso in caso contrario. (se il file non esisteva, il valore restituito non è specificato)

rs.rmdir(filename)
Rimuove una directory dalla scheda SD. La directory deve essere vuota. Restituisce vero se la rimozione della directory è riuscita, falso in caso contrario. (se la directory non esiste, il valore restituito non è specificato)

file.name()
Restituisce il nome del file

file.available()
Controlla se ci sono byte disponibili per la lettura dal file. Restituisce il numero di byte.

file.close()
Chiude il file e assicura che tutti i dati scritti su di esso siano salvati fisicamente sulla scheda SD.

file.flush()
Assicura che tutti i byte scritti nel file vengano salvati fisicamente sulla scheda SD. Questo viene fatto automaticamente quando il file viene chiuso.

file.peek()
Legge un byte dal file senza passare a quello successivo. Cioè, le successive chiamate a peek() restituiranno lo stesso valore, così come la successiva chiamata a read().

file.position()
Ottiene la posizione corrente all’interno del file (cioè la posizione in cui verrà letto o scritto il byte successivo). Restituisce la posizione all’interno del file ( unsigned long ).

file.print(data) file.print(data, base)
Stampa i dati nel file, che deve essere stato aperto per la scrittura. Stampa i numeri come una sequenza di cifre, ciascuna un carattere ASCII (ad es. il numero 123 viene inviato come i tre caratteri ‘1’, ‘2’, ‘3’). Dati parametro: i dati da stampare (char, byte, int, long o string), BASE (opzionale): la base in cui stampare i numeri: BIN per binario (base 2), DEC per decimale (base 10), OCT per ottale (base 8), HEX per esadecimale (base 16). Restituisce il numero di byte scritti, sebbene la lettura di tale numero sia facoltativa.

file.println() file.println(data) file.println(data, base)
Come print ma con ritorno finale

file.seek(pos)
Cerca una nuova posizione nel file, che deve essere compresa tra 0 e la dimensione del file (inclusa). Parametri: pos: la posizione in cui cercare ( unsigned long ). Restituisce true in caso di successo, false in caso di fallimento (boolean)

file.size()
Ottieni la dimensione del file. Restituisce la dimensione del file in byte ( unsigned long ).

file.read()  file.read(buf, len)
Legge dal file. Restituisce il byte (o carattere) successivo o -1 se non è disponibile.

file.write(data) file.write(buf, len)
Scrive i dati nel file. Restituisce il numero di byte scritti, sebbene la lettura di tale numero sia facoltativa

file.isDirectory()
Directory (o cartelle) sono tipi speciali di file, questa funzione segnala se il file corrente è una directory o meno. Restituisce true se è directory.

file.openNextFile()
Riporta il file o la cartella successiva in una directory. Restituisce il file o la cartella successiva nel percorso.

file.rewindDirectory()
Ti riporterà al primo file nella directory, usato insieme a openNextFile().

SD.begin(uint8_t csPin, SPISettings cfg = SPI_HALF_SPEED)
SDFS.setConfig(SDFSConfig(csPin, cfg))

È possibile impostare ulteriori informazioni e parametri ereditati dalla classe SDFS.

SDFS.format()
Formatta il file system. Restituisce vero se la formattazione ha avuto successo.

SDFS.open(path, mode)
Apre un file. path deve essere un percorso assoluto che inizia con una barra (ad esempio /dir/nomefile.txt). mode è una stringa che specifica la modalità di accesso. Può essere uno tra “r”, “w”, “a”, “r+”, “w+”, “a+”. Il significato di queste modalità è lo stesso della funzione fopen C.
Restituisce l’oggetto File. Per verificare se il file è stato aperto correttamente, utilizzare l’operatore booleano.

SDFS.exists(path)
Restituisce vero se esiste un file con un determinato percorso, falso in caso contrario.

SDFS.openDir(path)
Apre una directory dato il suo percorso assoluto. Restituisce un oggetto Dir.

SDFS.remove(path)
Elimina il file dato il suo percorso assoluto. Restituisce true se il file è stato eliminato correttamente.

SDFS.rename(pathFrom, pathTo)
Rinomina il file da pathFrom a pathTo. I percorsi devono essere assoluti. Restituisce true se il file è stato rinominato correttamente.

SDFS.info(fs_info)
Riempie la struttura FSInfo con informazioni sul file system. Restituisce true in caso di successo, false in caso contrario.

file.getCreationTime()
Restituisce la data di creazione in epoch time.

file.getLastWrite()
Restituisce i dati dell’ultima scrittura/modifica in epoch time.

Codice

Raspberry Pi Pico, W and rp2040: SD Card module SPI
Raspberry Pi Pico, W and rp2040: SD Card module SPI

Ora alcuni esempi con il cablaggio di default (SPI).

Ottieni tutte le informazioni e l’elenco dei file della scheda SD

/*
  SD card test for Raspberry Pi Pico or rp2040 boards

  This example shows how use the utility libraries

  The circuit:
    SD card attached to SPI bus as follows:
	#define PIN_SPI0_MISO  (16u)
	#define PIN_SPI0_MOSI  (19u)
	#define PIN_SPI0_SCK   (18u)
	#define PIN_SPI0_SS    (17u)
*/
// include the SD library:
#include <SPI.h>
#include <SD.h>

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(SS)) {
    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.");
  }
  // 0 - SD V1, 1 - SD V2, or 3 - SDHC/SDXC
  // print the type of card
  Serial.println();
  Serial.print("Card type:         ");
  switch (SD.type()) {
    case 0:
      Serial.println("SD1");
      break;
    case 1:
      Serial.println("SD2");
      break;
    case 3:
      Serial.println("SDHC/SDXC");
      break;
    default:
      Serial.println("Unknown");
  }

  Serial.print("Cluster size:          ");
  Serial.println(SD.clusterSize());
  Serial.print("Blocks x Cluster:  ");
  Serial.println(SD.blocksPerCluster());
  Serial.print("Blocks size:  ");
  Serial.println(SD.blockSize());

  Serial.print("Total Blocks:      ");
  Serial.println(SD.totalBlocks());
  Serial.println();

  Serial.print("Total Cluster:      ");
  Serial.println(SD.totalClusters());
  Serial.println();

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

  volumesize = SD.totalClusters();
  volumesize *= SD.clusterSize();
  volumesize /= 1000;
  Serial.print("Volume size (Kb):  ");
  Serial.println(volumesize);
  Serial.print("Volume size (Mb):  ");
  volumesize /= 1024;
  Serial.println(volumesize);
  Serial.print("Volume size (Gb):  ");
  Serial.println((float)volumesize / 1024.0);

  Serial.print("Card size:  ");
  Serial.println((float)SD.size()/1000);

  FSInfo fs_info;
  SDFS.info(fs_info);

  Serial.print("Total bytes: ");
  Serial.println(fs_info.totalBytes);

  Serial.print("Used bytes: ");
  Serial.println(fs_info.usedBytes);

  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');
    }
    Serial.print(entry.name());
    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);
      time_t cr = entry.getCreationTime();
      time_t lw = entry.getLastWrite();
      struct tm * tmstruct = localtime(&cr);
      Serial.printf("\tCREATION: %d-%02d-%02d %02d:%02d:%02d", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, tmstruct->tm_min, tmstruct->tm_sec);
      tmstruct = localtime(&lw);
      Serial.printf("\tLAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, tmstruct->tm_min, tmstruct->tm_sec);
    }
    entry.close();
  }
}

E il risultato sarà

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

Card type:         SDHC/SDXC
Cluster size:          32768
Blocks x Cluster:  64
Blocks size:  512
Total Blocks:      7451

Total Cluster:      476864

Volume type is:    FAT32
Volume size (Kb):  2740977
Volume size (Mb):  2676
Volume size (Gb):  2.61
Card size:  2740977.75
Total bytes: 2740977664
Used bytes: 163840
System Volume Information/
	WPSettings.dat		12	CREATION: 2022-07-07 16:19:38	LAST WRITE: 2022-07-07 16:19:40
	IndexerVolumeGuid		76	CREATION: 2022-07-07 16:19:40	LAST WRITE: 2022-07-07 16:19:42
testSD.txt		40	CREATION: 1980-01-01 00:00:02	LAST WRITE: 1980-01-01 00:00:02

Scrivi e leggi un file dalla scheda SD

Ecco un semplice sketch che scrive e legge un file.

/*
  SD card read/write

  This example shows how to read and write data to and from an SD card file
  The circuit:
   SD card attached to SPI bus as follows:
	#define PIN_SPI0_MISO  (16u)
	#define PIN_SPI0_MOSI  (19u)
	#define PIN_SPI0_SCK   (18u)
	#define PIN_SPI0_SS    (17u)

*/

#include <SPI.h>
#include <SD.h>

File myFile;

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("Initializing SD card...");

  if (!SD.begin(SS)) {
    Serial.println("initialization failed!");
    while (1);
  }
  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
}

Ecco l’output seriale.

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

Grazie

  1. Schede Raspberry Pi Pico e rp2040: pinout, specifiche e configurazione IDE Arduino
  2. Schede Raspberry Pi Pico e rp2040: filesystem LittleFS integrato
  3. Scheda Raspberry Pi Pico e rp2040: ethernet w5500 e requests HTTP e HTTPS (SSL)
  4. Schede Raspberry Pi Pico e rp2040: WiFiNINA con coprocessore WiFi ESP32
  5. Schede Raspberry Pi Pico e rp2040: come utilizzare una scheda SD

Spread the love

Una risposta

  1. Gambuzzi ha detto:

    Molto interessante

Lascia un commento

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