SPI (Interfaz Periférica Serial)#

Advertencia

El soporte para la tarjeta de desarrollo Cocket Nova (CH552) se encuentra en desarrollo. Por favor, mantente atento a futuras actualizaciones.

Visión General del SPI#

SPI (Interfaz Periférica Serial) es un bus de comunicación síncrono, full-duplex y maestro-esclavo. Se utiliza comúnmente para conectar microcontroladores a periféricos como sensores, pantallas y dispositivos de memoria. La placa de desarrollo DualMCU ONE ofrece capacidades de comunicación SPI, lo que te permite interconectar una amplia gama de dispositivos SPI.

A continuación, se muestra la tabla de asignación de pines para las conexiones SPI en la DualMCU ONE, detallando las conexiones GPIO correspondientes para los microcontroladores ESP32 y RP2040.

Tabla 8 Asignación de Pines SPI#

PIN

GPIO ESP32

GPIO RP2040

SCK

18

18

MISO

19

16

MOSI

23

19

SS

5

17

DualMCU ONE RP2040 y ESP32#

La DualMCU ONE cuenta con interconexiones SPI cruzadas entre los microcontroladores ESP32 y RP2040. Esto significa que el RP2040 actúa como maestro y el ESP32 como esclavo. La tabla a continuación detalla la configuración de los pines GPIO para la comunicación SPI cruzada entre el ESP32 y el RP2040.

Tabla 9 Configuración SPI cruzado entre ESP32 y RP2040#

ESP32

GPIO

RP2040

GPIO

SCK

18

SCK

14

MISO

23

MOSI

15

MOSI

19

MISO

12

SS

5

SS

13

from machine import Pin, SPI
import time

# RP2040 pin configuration
rp2040_cs = Pin(13, Pin.OUT)   # Chip Select (CS)
spi = SPI(1, baudrate=1000000, polarity=0, phase=0, sck=Pin(14), mosi=Pin(15), miso=Pin(12))

# rp2040_cs = Pin(21, Pin.OUT)   # Chip Select (CS)
# spi = SPI(0, baudrate=100000, polarity=0, phase=0, sck=Pin(18), mosi=Pin(19), miso=Pin(20))


def spi_send_command(command, response_length=3):
    # Seleccionar el dispositivo SPI (bajar CS)
    rp2040_cs.value(0)
    time.sleep_us(10)  # Breve pausa para estabilizar CS

    # Asegurarte de que ambos buffers (envío y respuesta) sean del mismo tamaño
    send_buffer = bytearray(response_length)
    response = bytearray(response_length)  # Buffer de respuesta de 2 bytes, igual que send_buffer

    # Copiar el comando en el buffer de envío (solo el primer byte)
    send_buffer[0] = command[0]

    # Enviar el comando y recibir la respuesta
    spi.write_readinto(send_buffer, response)

    # Deseleccionar el dispositivo SPI (subir CS)
    time.sleep_us(1)
    rp2040_cs.value(1)

    return response

def main():
    # Lista de comandos para enviar al ESP32 (cada comando es de 1 byte)
    commands = [
        b'\x01',  # Comando 0x01: Solicitar estado general de memoria
        b'\x02',  # Comando 0x02: Solicitar memoria libre
        b'\x03',  # Comando 0x03: Solicitar estado de los registros
        b'\x21'   # Comando 0x04: Solicitar temperatura
    ]

    while True:
        for command_to_send in commands:
            # Enviar el comando y recibir la respuesta
            response = spi_send_command(command_to_send, response_length=3)  # Ajustado para 2 bytes

            # Imprimir el comando enviado y la respuesta recibida
            print(f"Sent command: {command_to_send.hex()} | Received response:", response)

            time.sleep(1)  # Tiempo de espera entre comandos

main()

SDCard SPI#

Advertencia

Asegúrate de que la Micro SD contenga datos. Se recomienda guardar múltiples archivos de antemano para facilitar su uso.

Biblioteca (MicroPython)#

La biblioteca dualmcu.py para MicroPython en ESP32 y RP2040 es compatible con la lectura y escritura en la Micro SD. La biblioteca proporciona una interfaz sencilla para leer y escribir archivos en la tarjeta SD. La biblioteca está disponible en PyPi y se puede instalar mediante el IDE Thonny.

Biblioteca Enlace
ocks Ejemplo de instalación
dualmcu Biblioteca DualMCU

VSPI y HSPI#

La diferencia entre VSPI y HSPI radica en la velocidad de transferencia de datos. VSPI es más lento que HSPI, pero es más fácil de configurar. HSPI, por otro lado, es más rápido pero requiere una configuración más detallada.

Interfaz VSPI#

Lector externo de Micro SD

Figura 21 Lector externo de Micro SD#

Las conexiones son las siguientes:

Esta tabla ilustra las conexiones entre la tarjeta SD y los pines GPIO en los microcontroladores ESP32 y RP2040.

Tabla 10 Conexiones VSPI#

Tarjeta SD

Nombre del Pin

ESP32

RP2040

D3

SS

5

17

CMD

MOSI

23

19

VSS

GND

VDD

3.3V

CLK

SCK

18

18

D0

MISO

19

16

Descripciones#
  • SCK (Reloj Serial)

  • SS (Selección del Esclavo)

import machine, os
from dualmcu import *

SCK_PIN = 18
MOSI_PIN = 23
MISO_PIN = 19
CS_PIN = 5

spi = machine.SPI(1, baudrate=100000, polarity=0, phase=0, sck=machine.Pin(SCK_PIN), mosi=machine.Pin(MOSI_PIN), miso=machine.Pin(MISO_PIN))
spi.init()
sd = SDCard(spi, machine.Pin(CS_PIN))
os.mount(sd, '/sd')
os.listdir('/')

print("archivos ...")
print(os.listdir("/sd"))

Interfaz HSPI#

Esta tabla detalla las conexiones entre la tarjeta SD y el microcontrolador ESP32.

Tabla 11 Conexiones HSPI#

Tarjeta SD

ESP32

PIN

D2

D3

SS (Selección del Esclavo)

15

CMD

MOSI

13

VSS

GND

VDD

3.3V

CLK

SCK (Reloj Serial)

14

VSS

GND

D0

MISO

12

D1

Para la prueba, utilizaremos un ESP32 WROM-32E y una tarjeta SanDisk Micros Ultra con una capacidad de 32 GB.

import machine
import os
from dualmcu import *

# Inicializa la interfaz SPI para la tarjeta SD
spi = machine.SPI(2, baudrate=1000000, polarity=0, phase=0, sck=machine.Pin(14), mosi=machine.Pin(13), miso=machine.Pin(12))

# Inicializa la tarjeta SD
sd = SDCard(spi, machine.Pin(15))

# Monta el sistema de archivos
vfs = os.VfsFat(sd)
os.mount(vfs, "/sd")

# Lista los archivos en la raíz de la tarjeta SD
print("Archivos en la raíz de la tarjeta SD:")
print(os.listdir("/sd"))

os.umount("/sd")

SDIO (Interfaz de Entrada/Salida Segura Digital)#

SDIO (Secure Digital Input/Output) es una interfaz de comunicación de alta velocidad que permite la transferencia de datos entre un microcontrolador y una tarjeta SD. La placa de desarrollo DualMCU ONE ofrece soporte para la interfaz SDIO, lo que te permite leer y escribir datos en una tarjeta SD de forma rápida y eficiente.

La interfaz SDIO es una interfaz de comunicación de alta velocidad que permite la transferencia de datos entre un microcontrolador y una tarjeta SD. La placa de desarrollo DualMCU ONE ofrece soporte para la interfaz SDIO, lo que te permite leer y escribir datos en una tarjeta SD de forma rápida y eficiente.

Pinout de la Micro SD

Figura 22 Pinout de la Micro SD#

Esta tabla detalla las conexiones entre la tarjeta SD y el microcontrolador ESP32.

Tabla 12 Conexiones SDIO#

Nombre del Pin

Pines Correspondientes en Modo SPI

Número GPIO (Slot 1)

Número GPIO (Slot 2)

CLK

SCLK

6

14

CMD

MOSI

11

15

DAT0

MISO

7

2

DAT1

Interrupción

8

4

DAT2

Sin Conexión (pullup)

9

12

DAT3

CS

10

13

Ejemplo de aplicación#

Lector de Micro SD

Figura 23 Lector de Micro SD#

/* Sketch for testing the ESP32 HSPI interface on the DualMCU ONE.
 * Connect the SD card to the following pins:
 *
 * SD Card | ESP32
 *    D2       12
 *    D3       13
 *    CMD      15
 *    VSS      GND
 *    VDD      3.3V
 *    CLK      14
 *    VSS      GND
 *    D0       2  (add 1K pull up after flashing)
 *    D1       4
 */

#include "FS.h"
#include "SD_MMC.h"

void listDir(fs::FS &fs, const char * dirname, uint8_t levels){
    Serial.printf("Listing directory: %s\n", dirname);

    File root = fs.open(dirname);
    if(!root){
        Serial.println("Failed to open directory");
        return;
    }
    if(!root.isDirectory()){
        Serial.println("Not a directory");
        return;
    }

    File file = root.openNextFile();
    while(file){
        if(file.isDirectory()){
            Serial.print("  DIR : ");
            Serial.println(file.name());
            if(levels){
                listDir(fs, file.path(), levels -1);
            }
        } else {
            Serial.print("  FILE: ");
            Serial.print(file.name());
            Serial.print("  SIZE: ");
            Serial.println(file.size());
        }
        file = root.openNextFile();
    }
}

void createDir(fs::FS &fs, const char * path){
    Serial.printf("Creating Dir: %s\n", path);
    if(fs.mkdir(path)){
        Serial.println("Dir created");
    } else {
        Serial.println("mkdir failed");
    }
}

void removeDir(fs::FS &fs, const char * path){
    Serial.printf("Removing Dir: %s\n", path);
    if(fs.rmdir(path)){
        Serial.println("Dir removed");
    } else {
        Serial.println("rmdir failed");
    }
}

void readFile(fs::FS &fs, const char * path){
    Serial.printf("Reading file: %s\n", path);

    File file = fs.open(path);
    if(!file){
        Serial.println("Failed to open file for reading");
        return;
    }

    Serial.print("Read from file: ");
    while(file.available()){
        Serial.write(file.read());
    }
}

void writeFile(fs::FS &fs, const char * path, const char * message){
    Serial.printf("Writing file: %s\n", path);

    File file = fs.open(path, FILE_WRITE);
    if(!file){
        Serial.println("Failed to open file for writing");
        return;
    }
    if(file.print(message)){
        Serial.println("File written");
    } else {
        Serial.println("Write failed");
    }
}

void appendFile(fs::FS &fs, const char * path, const char * message){
    Serial.printf("Appending to file: %s\n", path);

    File file = fs.open(path, FILE_APPEND);
    if(!file){
        Serial.println("Failed to open file for appending");
        return;
    }
    if(file.print(message)){
        Serial.println("Message appended");
    } else {
        Serial.println("Append failed");
    }
}

void renameFile(fs::FS &fs, const char * path1, const char * path2){
    Serial.printf("Renaming file %s to %s\n", path1, path2);
    if (fs.rename(path1, path2)) {
        Serial.println("File renamed");
    } else {
        Serial.println("Rename failed");
    }
}

void deleteFile(fs::FS &fs, const char * path){
    Serial.printf("Deleting file: %s\n", path);
    if(fs.remove(path)){
        Serial.println("File deleted");
    } else {
        Serial.println("Delete failed");
    }
}

void testFileIO(fs::FS &fs, const char * path){
    File file = fs.open(path);
    static uint8_t buf[512];
    size_t len = 0;
    uint32_t start = millis();
    uint32_t end = start;
    if(file){
        len = file.size();
        size_t flen = len;
        start = millis();
        while(len){
            size_t toRead = len;
            if(toRead > 512){
                toRead = 512;
            }
            file.read(buf, toRead);
            len -= toRead;
        }
        end = millis() - start;
        Serial.printf("%u bytes read for %u ms\n", flen, end);
        file.close();
    } else {
        Serial.println("Failed to open file for reading");
    }


    file = fs.open(path, FILE_WRITE);
    if(!file){
        Serial.println("Failed to open file for writing");
        return;
    }

    size_t i;
    start = millis();
    for(i=0; i<2048; i++){
        file.write(buf, 512);
    }
    end = millis() - start;
    Serial.printf("%u bytes written for %u ms\n", 2048 * 512, end);
    file.close();
}

void setup(){
    Serial.begin(115200);
    if(!SD_MMC.begin()){
        Serial.println("Card Mount Failed");
        return;
    }
    uint8_t cardType = SD_MMC.cardType();

    if(cardType == CARD_NONE){
        Serial.println("No SD_MMC card attached");
        return;
    }

    Serial.print("SD_MMC Card Type: ");
    if(cardType == CARD_MMC){
        Serial.println("MMC");
    } else if(cardType == CARD_SD){
        Serial.println("SDSC");
    } else if(cardType == CARD_SDHC){
        Serial.println("SDHC");
    } else {
        Serial.println("UNKNOWN");
    }

    uint64_t cardSize = SD_MMC.cardSize() / (1024 * 1024);
    Serial.printf("SD_MMC Card Size: %lluMB\n", cardSize);

    listDir(SD_MMC, "/", 0);
    createDir(SD_MMC, "/mydir");
    listDir(SD_MMC, "/", 0);
    removeDir(SD_MMC, "/mydir");
    listDir(SD_MMC, "/", 2);
    writeFile(SD_MMC, "/hello.txt", "Hello ");
    appendFile(SD_MMC, "/hello.txt", "World!\n");
    readFile(SD_MMC, "/hello.txt");
    deleteFile(SD_MMC, "/foo.txt");
    renameFile(SD_MMC, "/hello.txt", "/foo.txt");
    readFile(SD_MMC, "/foo.txt");
    testFileIO(SD_MMC, "/test.txt");
    Serial.printf("Total space: %lluMB\n", SD_MMC.totalBytes() / (1024 * 1024));
    Serial.printf("Used space: %lluMB\n", SD_MMC.usedBytes() / (1024 * 1024));
}

void loop(){

}