Error al iniciar la tarjeta microSD en modo SPI

Estoy tratando de conectar una tarjeta microSD de 32 GB con PIC32MX795F512L mediante comunicación SPI. La comunicación SPI funciona bien, ya que la probé cortando la línea de entrada y salida de datos y recibiendo los datos que envié. Estoy siguiendo los ejemplos proporcionados en el libro de Lucio de Jasio, Programación de microcontroladores de 32 bits en C.

Las siguientes son las tareas que estoy realizando:

1. Initializing the SPI Communication in initSPI(), setting the baudrate=76 (250 kHz)
2. Initializing the SD card
   -> CS = 1
   -> Sending 80 clock cycles to wake the card
   -> CS = 0
   -> Sending CMD0 command (0x40, 0x00, 0x00, 0x00, 0x00, 0x95)

Pero el problema es que no recibo la R1respuesta de la tarjeta, sino que recibo el 0xFF. El siguiente es el código:

#define FCY 77000000UL
#define FPB (FCY/2)
#define BAUDRATE    9600
#pragma config POSCMOD=HS,FNOSC=PRIPLL
#pragma config FPLLIDIV=DIV_3, FPLLMUL=MUL_21, FPLLODIV=DIV_1
#pragma config FPBDIV=DIV_2, FWDTEN=OFF

#include <stdio.h>
#include <stdlib.h>
#include <plib.h>
#include <stdbool.h>

#define SDI  _RC4
#define SDCS _RA9
#define enableSD()    SDCS = 0
#define disableSD()   SDCS = 1
#define readSPI() writeSPI(0xFF)

int main(int argc, char** argv)
{
    TRISAbits.TRISA9 = 0;   // CS as output
    TRISCbits.TRISC4 = 1;   // SDI as input
    TRISDbits.TRISD0 = 0;   // SDO as output
    TRISDbits.TRISD10 = 0;  // SCK as output
    AD1PCFG = 0xFFFF;
    DDPCONbits.JTAGEN = 0;
    OpenUART1( UART_EN | UART_NO_PAR_8BIT | UART_1STOPBIT, UART_RX_ENABLE | UART_TX_ENABLE, (FPB/16/BAUDRATE)-1 );
    disableSD(); //CS PIN INITIALLY HIGH TO DISABLE CARD
    initSPI();
    initSD();

    while(1)
    {

    }
    return (EXIT_SUCCESS);
}

void initSPI( void)
{
    SPI1BRG = 76; // FPB/154=250KHZ; (76+1)*2 = 154, so brg = 76
    SPI1CONbits.MSTEN = 1; // MasterEnable
    SPI1CONbits.CKE = 1;   // CKE on
    SPI1CONbits.ON = 1;    // SPI Module ON
}

unsigned char writeSPI(unsigned int b)
{
    SPI1BUF=b; // Write to buffer for TX
    while( !SPI1STATbits.SPIRBF)
        ; // Wait transfer complete
    return(SPI1BUF);
}

int initSD(void)
{
    int i,r,rx;
    int data;

    // Step1: Disable SD card
    disableSD();

    // Step2: Send 80 clock cycles to wake up the card
    for(i=0; i<=9; i++)
    {
        data = writeSPI(0xFF);
    }

    // Step3: Enable SD card
    enableSD();

    // Step4: Send CMD0 command to RESET
    r = sendCMD0();
    disableSD();
    if(r!=1)
    {
        putsUART1("CMD reject\n");
    }
}

int sendCMD0()
{
    int i,r;
    enableSD();

    writeSPI(0x40);
    writeSPI(0x00);
    writeSPI(0x00);
    writeSPI(0x00);
    writeSPI(0x00);
    writeSPI(0x95); //CMD0 Command

    for( i=0; i<100; i++)
    {
        r = readSPI();
        if ((r & 0x80) == 0)
            break;
    }
    return ( r);
}

Aquí está el esquema :

los esquemas

No estoy recibiendo la R1respuesta. Está atascado en el paso 4 en initSD().

¿Hay alguna manera de saber si la tarjeta se ha despertado o no después de enviar 80 pulsos de reloj? Después de 80 pulsos de reloj, la respuesta de la tarjeta es 0xFF.

¿Necesito reducir la frecuencia del reloj? Actualmente es de 250 kHz.

¿Por qué la tarjeta no se inicializa? ¿Cómo puedo arreglarlo?

Puede comparar con mi biblioteca en Github ( encabezado , código ) y ver cuáles son las diferencias. He probado en un número decente de tarjetas para resolver casos extremos.
Buscaré la referencia (no la vi ayer), pero por lo que recuerdo, las SdCards necesitan una tapa de desacoplamiento fuerte: 10uF // 100nF cerca del soporte. Busque en Google el siguiente "condensador de desacoplamiento para SdCard". Probablemente no necesite ir más allá de la página de Google :-)
es posible que desee consultar < microchip.com/forums/m530149.aspx > que tiene código, en C, para acceder al chip en modo SPI. Es demasiado largo para publicar como respuesta, o lo publicaría como respuesta.
@user3629249 ¿puede compartir el enlace nuevamente? me dice error interno del servidor
¿Has probado con otra tarjeta? También deshazte de todos los ints. Use caracteres sin firmar o uint8_t en todas partes. También haga que el UART imprima los valores de r devueltos para que podamos ver qué está pasando.

Respuestas (2)

No veo ningún código para hacer que el pin MISO sea un pin de entrada o para hacer que el pin SCK salga. Como no está utilizando la biblioteca SPI en plib, debe realizar toda la inicialización del pin usted mismo. Además, después de enviar CMD0, aumente el número de iteraciones (en su forciclo) de 8 a 20 (yo uso 100), para proporcionar suficiente margen de maniobra. También use esta comparación en su lugar:

if ((r & 0x80) == 0) 
   break;
return r;

También haz run unsigned charpor todas partes.

Ohh gracias por sugerir eso. Pensé que después de inicializar el SPI, todos los sck SDO SDI se configuran automáticamente. Ahora haré salida SDO y SCK y SDI como entrada.
Por qué está usando esta condición si: if ((r & 0x80) == 0). ¿Puedes explicar un poco?
El primer bit de la respuesta de las tarjetas SD es siempre un cero. Entonces, uno debe verificar el primer bit de cualquier byte recibido para un cero. Su código original hace más o menos lo mismo que el mío, pero he estado usando el mío durante un tiempo con PIC, así que creo que el mío es más confiable :)
uint8_tsería mejor que unsigned char.
Usé su condición if pero la tarjeta no responde 0x01. ¿Hay algún error en mis funciones de transmisión de datos? ¿Puedes echarle un vistazo? ¿Hay algún otro comando que pueda enviar antes de CMD0 para verificar otras cosas?
¿Puedes actualizar el código en tu pregunta? ¿Después de hacer todos los pines?
@TisteAndii He actualizado mi código.
Cambia todos tus ints a uint8_t. Haga explícitamente SPICON1bits.CKP = 0.
También ponga SPI1CON = 0 en la parte superior de init_spi(). Y luego pruebe con otra tarjeta SD. No hay otras ideas aquí :(
Está bien, pondré SPI1CON =0 y probaré con eso. Gracias
Y hacer los cambios de tipo y la asignación de CKP
@TisteAndii No soy muy experto en la programación de tarjetas SD, pero cuando inicializamos el módulo SPI, todos los pines relativos se configuran automáticamente.
Solo si está utilizando las bibliotecas periféricas. Esos no se usan aquí. Inspeccione open_spi.c en el plib y verá las mismas asignaciones de pines allí. Aquí, plib no se usa para SPI

Se ve en la superficie como un problema eléctrico

  • Pruebe una velocidad más lenta para comenzar; es posible que tenga una capacitancia excesiva en sus líneas o una tarjeta SD de baja velocidad.
  • ¿Está sondeando hasta que la tarjeta esté lista (ver más abajo)?
  • ¿Puedes ponerle un alcance para verificar los tiempos, estos son críticos?
  • Pruebe con una tarjeta SD diferente o con la misma tarjeta en un dispositivo que funcione
  • ¿Puede transmitir y recibir a un puerto diferente en el mismo dispositivo?
  • ¿Puede transmitir y recibir a una placa diferente utilizando el mismo dispositivo PIC?
  • ¿Está utilizando controladores de microchip / bibliotecas de software?
  • ¿Tienes MISO/MOSI correctamente cableado?
  • ¿Se cumplen los requisitos de potencia, tapas de desacoplamiento, etc.? 10uF+//100nF (algunas personas usan 100uF!!)
  • ¿Sus pines de salida y entrada están configurados correctamente?

En una etapa posterior, es posible que deba verificar lo siguiente: ningún orden en particular.

  • Compruebe el 'endianness' de su SPI
  • Consulta tu CDN
  • Verifique dos veces su protocolo

https://www.sdcard.org/downloads/pls/simplified_specs/archive/part1_110.pdf

La página 47 dice que necesita sondear el dispositivo hasta que la tarjeta esté lista. ¿Está haciendo esto?

Estoy usando 250 KHZ, definitivamente intentaré con una velocidad más lenta. Mi tarjeta Micro SD es de 32 gb clase 10 y está en buen estado de funcionamiento. PIC32 también funciona bien, he hecho muchos otros programas en él. No estoy usando bibliotecas de microchips y sí, las líneas MISO MOSI están cableadas correctamente. Creo que solo tenemos que configurar la línea CS manualmente y el resto de las líneas se configuran automáticamente. También he subido el esquema. Por favor, mire esto. no entendí me Check the 'endianness'puedes explicar un poco
endianness es la jerga para 'envías MSB primero o LSB primero'. Tal vez verifique su código de inicio de hardware / pin, con el que se proporciona en las bibliotecas de microchip. La mayor parte de mi trabajo es con ARM, por lo que lamentablemente no puedo ayudar con las especificaciones de PIC para la velocidad del pin, pullup/pulldown, etc. Pruebe un retraso después de configurar la línea CS.
Quiere decir que debería agregar un retraso después del paso 1 en initSD ()
@user007 sí, y sondéelo hasta que esté listo a intervalos de <50 ms. ver mis ediciones
No estoy sondeando, sino que estaba esperando una respuesta que permitiera un retraso de hasta 8 bytes. Lo sondearé hasta que su respuesta sea 0x01. Gracias
¿Hay alguna respuesta de la tarjeta después de enviar 80 pulsos de reloj?
El controlador que uso pone una espera de 1 mS después de encender (selección de chip) y luego envía CMD8, CMD55, CMD41. Se recomienda usar <24 MHz a menos que haga un 'cambio de alta velocidad'. Este es el modo SDIO (4 bits). El sondeo está en los CMD