Lese-/Schreibproblem der SD-Karte im SPI-Modus. (STM32) (CMD17, CMD24)

Guten Tag,

Ich verwende einen STM32F103C8T6 und versuche, eine SD-Karte (SanDisk Ultra 16 GB MicroSD HC-Karte) damit zu verbinden. Ich weiß, dass die Karte gut funktioniert, weil ich mit einem PC lesen und schreiben kann und sie funktioniert auch gut auf einem AVR-basierten Datenlogger, den ich gebaut habe (sowohl Arduino als auch Nicht-Arduino). Ich habe jedoch ein Problem mit meinem aktuellen Setup.

Einzelheiten:

  1. Ich verwende ein generisches STM32F103C8T6 Minimum System Development Board (bei 72 MHz). (Identisch mit der "blauen Pille".) (ähnlich: https://www.ebay.com/itm/STM32F103C8T6-ARM-STM32-Minimum-System-Development-Board-Module-For-Arduino-/311156408508 )Schema der STM32F103C8T6-Mindestsystementwicklungsplatine
  2. Catalex Micro-SD-Kartenmodul. (ähnlich: https://www.ebay.in/itm/Micro-SD-Card-Module-Reader-Slot-Socket-Shield-Arduino-ARM-MCU-Read-Write-SPI-/302383980892?_trksid=p2059707 .m48543.l9013 )Schaltplan des SD-Moduls
  3. Eine benutzerdefinierte (DIY)-Leiterplatte zur Unterbringung der Entwicklungsplatine und der damit verbundenen Verbindungen.Schaltplan der Kopfleiste

Im SD-Modul habe ich den Puffer, die Signalleitungswiderstände umgangen, den LDO-Regler entfernt und seinen Ein- und Ausgang kurzgeschlossen. Zusätzlich habe ich einen 100uF-Kondensator zwischen Strom und Masse des SD-Moduls angeschlossen. Verwenden Sie es im Grunde nur, um die Leitungen der microSD-Karte mit dem Controller zu verbinden.

Ich verwende STM32CubeMX, um den Start- und Initialisierungscode zu generieren, und verwende Keil MDK v5. Der SPI-Slave-Select-Pin wird manuell im Code gesteuert.

Die Karte scheint gut zu initialisieren (74+ Zyklen mit deaktiviertem SS, CMD0, CMD1, CMD8, ACMD41, CMD58, CMD16). (Obwohl die Init-Funktion einige Durchläufe benötigt und daher in einer While-Schleife in der Main-Funktion platziert wird). Die Taktfrequenz wird während der gesamten Initialisierungsfunktion auf ~140 kHz gehalten und danach auf ~4,5 MHz erhöht.

Die Karte antwortet sogar mit einer gültigen R1-Antwort von 0x00 auf den Lese-Einzelblock-Befehl (CMD17). (Benötigt aber eine merkliche Verzögerung für eine gültige Antwort). Das Datenblock-Startbyte 0xFE kommt jedoch nie an. Gelegentlich wird ein 0xFE abgefangen, aber alle gelesenen Daten sind nur eine Reihe von 0xFF, was mich zu der Annahme veranlasst, dass es sich beim Lesen wahrscheinlich nur um eine verpasste Uhr oder etwas Ähnliches handelt.

Die Karte antwortet auch mit einer gültigen R1-Antwort von 0x00 auf den Befehl zum Schreiben eines einzelnen Blocks (CMD24), aber nach dem Datenblock-Startbyte werden keine Daten geschrieben und ein Datenblock mit CRC wird übertragen.

CMD58 antwortet mit einer R1-Antwort von 0x00 und die OCR-Anzeige ist auch nur 0x00 0x00 0x00 0x00.

Ich schreibe meinen Initialisierungscode, Befehlssendefunktion, Blocklese- und Blockschreibcode auf:

Initialisierung:

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;

}

Befehle senden:

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;
}

Lesen blockieren:

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;
}

Schreiben blockieren:

 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;
}

Ich bin mit diesem Thema am Ende. Irgendwelche Ideen? Lassen Sie mich auch wissen, welche zusätzlichen Informationen erforderlich sind.

Hast du bisher nur diese eine Karte ausprobiert? Ich würde vorschlagen, dass Sie auch ein völlig anderes ausprobieren, da ich Situationen hatte, in denen eine bestimmte Marke / ein bestimmtes Kartenmodell einen schlecht implementierten SPI-Modus hatte (aber in SDIO gut funktionierte).
Ich habe bisher nur eine Karte ausprobiert. Ich melde mich wieder, nachdem ich eine andere Karte ausprobiert habe. Aber wie gesagt, ich glaube nicht, dass es an der Karte liegt, weil sie auf dem AVR gut funktioniert. Ich habe auch versucht, den gleichen Code aus dem AVR-Programm zu verwenden. (allerdings ohne Ergebnisse. Ich glaube nicht, dass ich das richtig portiert habe)
Also habe ich eine andere SD-Karte ausprobiert, das gleiche Problem :(

Antworten (1)

Wenn ich auf die Zustandsmaschine schaue, die ich vor einiger Zeit für Micro-SD-Karten im SPI-Modus entworfen habe, sehe ich die folgende Sequenz:

  • CMD0
  • CMD8
  • CMD55 + ACMD41 in Schleife, bis ACMD41 Erfolg zurückgibt
  • CMD58
  • CMD16

Wenn CMD55 jedoch einen Fehler zurückgibt, gehe ich zu CMD1 und versuche, die Karte als MMC-Typ zu initialisieren.

Es ist schwer zu sagen, was genau in Ihrem Fall passiert, aber ich denke, CMD1 darf nicht an dieser Stelle in Ihrer Liste der von Ihnen verwendeten Befehle stehen. Ich empfehle Ihnen, diese hervorragende Webseite von ELM ChaN gründlich zu studieren , auf der Folgendes steht:

Da ACMD41 anstelle von CMD1 für SDC empfohlen wird, ist es ideal, zuerst ACMD41 zu versuchen und es bei Ablehnung mit CMD1 erneut zu versuchen, um beide Kartentypen zu unterstützen.

Sie verwenden CMD1 und dann ACMD41, was die falsche Reihenfolge ist, und die Karte wechselt in einen Modus oder verursacht dieses von Ihnen beschriebene Verhalten. Sie erwähnen CMD55 auch nicht vor ACMD41, ich hoffe, Sie haben es an Ort und Stelle.