Problema de lectura/escritura del bloqueo de la tarjeta SD en modo SPI. (STM32) (CMD17, CMD24)

Buen día,

Estoy usando un STM32F103C8T6 e intento conectar una tarjeta SD (tarjeta SanDisk Ultra 16GB MicroSD HC) con él. Sé que la tarjeta funciona bien porque puedo leer, escribir usando una PC y también funciona bien en un registrador de datos basado en AVR que construí (tanto arduino como no arduino). Sin embargo, tengo un problema al hacer lo mismo con mi configuración actual.

Detalles:

  1. Estoy usando una placa de desarrollo de sistema mínimo STM32F103C8T6 genérica (a 72 MHz). (Idéntico a la "píldora azul".) (similar a: https://www.ebay.com/itm/STM32F103C8T6-ARM-STM32-Minimum-System-Development-Board-Module-For-Arduino-/311156408508 )Esquema mínimo de la placa de desarrollo del sistema STM32F103C8T6
  2. Módulo de tarjeta micro SD Catalex. (similar a: https://www.ebay.in/itm/Micro-SD-Card-Module-Reader-Slot-Socket-Shield-Arduino-ARM-MCU-Read-Write-SPI-/302383980892?_trksid=p2059707 .m48543.l9013 )esquema del módulo SD
  3. Una PCB personalizada (DIY) para albergar la placa de desarrollo y las conexiones a ella.esquema de tablero de cabecera

En el módulo SD, pasé por alto el búfer, las resistencias de cable de señal, eliminé el regulador LDO y cortocircuité su entrada y salida. Además, he conectado un condensador de 100 uF a través de la alimentación y la tierra del módulo SD. Básicamente, simplemente usándolo para conectar las líneas de tarjetas microSD al controlador.

Estoy usando STM32CubeMX para generar el código de inicio e inicialización y uso Keil MDK v5. El pin de selección de esclavo SPI se controla manualmente en código.

La tarjeta parece inicializarse bien (más de 74 ciclos con SS desactivado, CMD0, CMD1, CMD8, ACMD41, CMD58, CMD16). (Aunque la función init requiere algunas ejecuciones y, por lo tanto, se coloca en un bucle while en la función principal). La velocidad del reloj se mantiene en ~140 kHz durante la función de inicialización y aumenta a ~4,5 MHz después.

La tarjeta incluso responde con una respuesta R1 válida de 0x00 para leer un comando de bloque único (CMD17). (Pero necesita un retraso notable para una respuesta válida). Sin embargo, el byte de inicio del bloque de datos 0xFE nunca llega. Ocasionalmente detecta un 0xFE, pero cualquier lectura de datos es solo una serie de 0xFF, lo que me lleva a creer que probablemente sea solo un reloj perdido o algo así cuando se lee.

La tarjeta también responde con una respuesta R1 válida de 0x00 para escribir un comando de bloque único (CMD24), pero no se escribe ningún dato después del byte de inicio del bloque de datos y se transmite un bloque de datos con CRC.

CMD58 responde con una respuesta R1 de 0x00 y la lectura de OCR también es solo 0x00 0x00 0x00 0x00.

Estoy escribiendo mi código de inicialización, función de envío de comandos, lectura de bloque y código de escritura de bloque:

Inicialización:

uint8_t sd_ini(void)
{ 
  uint8_t i,cmd;
  int16_t tmr;
  LD_OFF;
  sdinfo.type = 0;
  uint8_t ocr[4];

  hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256; //140.625kbps
  HAL_SPI_Init(&hspi2);

  SS_SD_DESELECT();
  for(i=0;i<254;i++) //80 to init SD (74 min)
  {
        SPI_Release();
  }
  SS_SD_SELECT();
  //    while (SD_cmd(CMD0, 0) != 1);
  if(SD_cmd(CMD0, 0) == 1)// Enter Idle state
  {
        SPI_Release();
        if (SD_cmd(CMD8, 0x1AA) == 1) //SD v2
        {
            for (i = 0; i < 4; i++) 
            {
                ocr[i] = SPI_ReceiveByte();
            }
            sprintf(str1,"OCR: 0x%02X 0x%02X 0x%02X 0x%02Xrn",ocr[0],ocr[1],ocr[2],ocr[3]);
            HAL_UART_Transmit(&huart1,(uint8_t*)str1,strlen(str1),0x1000);
            // Get trailing return value of R7 resp
            if (ocr[2] == 0x01 && ocr[3] == 0xAA) // The card can work at vdd range of 2.7-3.6V
            {
                  for (tmr = 12000; tmr && SD_cmd(ACMD41, 1UL << 30); tmr--); // Wait for leaving idle state (ACMD41 with HCS bit)
                    if (tmr && SD_cmd(CMD58, 0) == 0) 
                    { // Check CCS bit in the OCR
                        for (i = 0; i < 4; i++) 
                        {
                            ocr[i] = SPI_ReceiveByte();
                        }
                        sprintf(str1,"OCR: 0x%02X 0x%02X 0x%02X 0x%02X\r\n",ocr[0],ocr[1],ocr[2],ocr[3]);
                        HAL_UART_Transmit(&huart1,(uint8_t*)str1,strlen(str1),0x1000);
                        sdinfo.type = (ocr[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2; // SDv2 (HC or SC)
                    }

            }
        }
        else //SD v1
        {
            if (SD_cmd(ACMD41, 0) <= 1)
            {
                sdinfo.type = CT_SD1; cmd = ACMD41; // SDv1
            }
            else
            {
                sdinfo.type = CT_MMC; cmd = CMD1; // MMCv3
            }
            for (tmr = 25000; tmr && SD_cmd(cmd, 0); tmr--) ; // Wait for leaving idle state
            if (!tmr || SD_cmd(CMD16, 512) != 0) // Set R/W block length to 512
            {
                sdinfo.type = 0;
            }
        }
  }
  else
  {
    return 1;
  }
    i=SD_cmd(CMD16, 512);
    hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
    HAL_SPI_Init(&hspi2);
    return 0;
    //SPI_SendByte(0x35); //test
    //SPI_SendByte(0x53); //test

    //LD_OFF;
    //return sd_raw_init()? 0:1;

}

Envío de comandos:

static uint8_t SD_cmd (uint8_t cmd, uint32_t arg)
{
    uint8_t n, res;
    // ACMD<n> is the command sequense of CMD55-CMD<n>
    if (cmd == ACMD41)
    {
        cmd &= 0x7F;
        res = SD_cmd(CMD55, 0);
        if (res > 1) 
        {
            return res;
        }
    }

    // Select the card  
    SS_SD_DESELECT();
    SPI_ReceiveByte();
    SS_SD_SELECT();
    SPI_ReceiveByte();

    // Send a command packet

    SPI_SendByte(cmd); // Start + Command index
    SPI_SendByte((uint8_t)(arg >> 24)); // Argument[31..24]
    SPI_SendByte((uint8_t)(arg >> 16)); // Argument[23..16]
    SPI_SendByte((uint8_t)(arg >> 8)); // Argument[15..8]
    SPI_SendByte((uint8_t)arg); // Argument[7..0]

    n = 0x01; // Dummy CRC + Stop
    if (cmd == CMD0) {n = 0x95;} // Valid CRC for CMD0(0)
    if (cmd == CMD8) {n = 0x87;} // Valid CRC for CMD8(0x1AA)
    SPI_SendByte(n);
    if(cmd==CMD17||cmd==CMD24)
         HAL_Delay(50); //returns 0xFF otherwise

    n = 20; // Wait for a valid response in timeout of 10 attempts
    do 
    {
        res = SPI_ReceiveByte();
        n--;
    } while ((res & 0x80)&&n);
    /*SS_SD_DESELECT();
    SPI_ReceiveByte();
    SS_SD_DESELECT();
    SPI_ReceiveByte();
    SS_SD_SELECT();*/
   return res;
}

Bloque de lectura:

uint8_t SD_Read_Block (uint8_t *buff, uint32_t lba)
{
    uint8_t result;
    uint16_t cnt;
    if(!SPI_wait_ready()) 
    {
        return 0;
    }
    result=SD_cmd (CMD17, lba); //CMD17 datasheet pg 50,96
    if (result!=0x00)
    {
        return 5; //exit if result isxt 0x00
    }
    //SPI_Release();
    cnt=0;
    do
    { //Waiting for the beginning of the block
        result=SPI_ReceiveByte();
        cnt++;
    } while ( (result!=0xFE)&&(cnt<0xFFFF) );

    if (cnt>=0xFFFF)
    {
        return 5;
    }   
    for (cnt=0;cnt<512;cnt++)
    {
        buff[cnt]=SPI_ReceiveByte(); //get the bytes of the block from the bus to the buffer
    }
    SPI_Release(); //We omit the checksum
    SPI_Release();
    return 0;
    //return sd_raw_read(lba, buff, 1)?0:1;
}

Escritura en bloque:

 uint8_t SD_Write_Block (uint8_t *buff, uint32_t lba)
 {
   uint8_t result;
   uint16_t cnt;

   if(!SPI_wait_ready()) 
   {
        return 0;
   }
   result=SD_cmd(CMD24,lba); //CMD24 datasheet page 51 and 97-98
   if (result!=0x00)
   { 
        return 6;
   } //Exit if the result is not 0x00
   SPI_Release();
   SPI_SendByte (0xFE); //Beginning of the buffer
   for (cnt=0;cnt<512;cnt++)
   {
       SPI_SendByte(buff[cnt]); //Send data
   }
   SPI_Release(); //leave crc
   SPI_Release();
   result=SPI_ReceiveByte();
   if ((result&0x05)!=0x05)
   { 
         return 6;
   } //Exit if the result is not 0x05 (Datasheet pg 111)
   cnt=0;
   do 
   { //Waiting for the end of the state BUSY
       result=SPI_ReceiveByte();
       cnt++;
   } while ( (result!=0xFF)&&(cnt<0xFFFF) );
   if (cnt>=0xFFFF)
   { 
       return 6;
   }
   return 0;
   //return sd_raw_write(lba, buff, 1)?0:1;
}

Estoy en el ingenio final con este problema. ¿Algunas ideas? También déjame saber cualquier información adicional requerida.

¿Has probado solo esa tarjeta hasta ahora? Le sugiero que también pruebe uno completamente diferente, ya que he tenido situaciones en las que una marca/modelo particular de tarjeta tenía un modo SPI mal implementado (pero funcionó bien en SDIO).
He probado solo una tarjeta hasta ahora. Informaré después de probar con otra tarjeta. Pero como dije, no creo que sea la tarjeta porque funciona bien en el AVR. También he intentado usar el mismo código del programa AVR. (Sin embargo, sin resultados. No creo que lo haya portado correctamente)
Así que probé con otra tarjeta SD, el mismo problema :(

Respuestas (1)

Mirando la máquina de estado que diseñé hace algún tiempo para la tarjeta micro-SD en modo SPI, veo la siguiente secuencia:

  • CMD0
  • CMD8
  • CMD55 + ACMD41 en bucle hasta que ACMD41 devuelva el éxito
  • CMD58
  • CMD16

Sin embargo, si CMD55 devuelve un error, busco CMD1 para intentar inicializar la tarjeta como tipo MMC.

Es difícil decir qué está sucediendo exactamente en su caso, pero creo que CMD1 no debe estar en este lugar en su lista de comandos que utilizó. Le recomiendo que estudie a fondo esta excelente página web de ELM ChaN, que dice lo siguiente:

Debido a que se recomienda ACMD41 en lugar de CMD1 para SDC, probar ACMD41 primero y volver a intentar con CMD1 si se rechaza, es ideal para admitir ambos tipos de tarjetas.

Está utilizando CMD1 y luego ACMD41, que es la secuencia incorrecta, y la tarjeta pasa a algún modo o provoca este comportamiento que describe. Tampoco menciona CMD55 antes de ACMD41, espero que lo tenga en su lugar.