MicroSD-Raw-Schreib-/Lesemodus. Nachdem der Sektor einmal geschrieben wurde, können keine weiteren Sektoren geschrieben und immer 0 gelesen werden

Ich mache zum ersten Mal die Schnittstelle zwischen einem Mikrocontroller (PIC32MX) und einer microSD-Karte über SPI. Die von mir verwendeten Karten werden korrekt initialisiert. Die Sektor-/Blockgröße ist auf 512 Bytes/Sektor festgelegt, da ich nur SDHC- und SDXC-Karten verwenden werde. Ich mache rohes Schreiben/Lesen auf der Karte, weil ich für meine Anwendung kein Dateisystem benötige, es ist ein einfacher Datenlogger. Ich kann den Inhalt der Kartensektoren mit der Software "Hex Workshop Hex Editor" überprüfen. Der SPI-Takt liegt derzeit als Ausgangspunkt bei 200 kHz.

Ich habe eine Funktion, die einen ganzen Sektor schreibt, und eine Funktion, die einen ganzen Sektor liest. Mir ist aufgefallen, dass, wenn ich die Sector_write-Funktion mit CMD24 (WRITE_BLOCK) mindestens einmal verwende (und es funktioniert beim ersten Aufruf, den Sektor korrekt schreiben), die nachfolgenden Sector_write-Aufrufe nicht funktionieren, ich sehe immer 0, die bei Sekunde gespeichert werden Sektor, in den ich schreiben möchte. In diesem Beispiel versuche ich, Sektor 1000 und 1300 mit demselben Inhalt zu füllen, aber nur Sektor 1000 wird mit diesem Inhalt gefüllt, und Sektor 1300 wird mit Nullen gefüllt (eigentlich sein ursprünglicher Inhalt). Außerdem habe ich versucht, die Sektoren 1000 und 1001 in einer Sequenz zu schreiben, und das Ergebnis ist dasselbe (Sektor 1001 füllt sich nicht mit demselben Inhalt wie Sektor 1000).

Das heißt, wenn ich die Funktion sector_write einmal aufrufe , gibt die Lesefunktion Lesevorgänge aus einem Sektor voller 0 (falsch) zurück. Wenn ich alle Aufrufe von write_sector kommentiere, erfolgen die Lesevorgänge korrekt.

Für den folgenden Code wird nur Sektor 1000 geschrieben ... der gesamte Sektor 1300 bleibt auf 0s. Dieser Code wird unmittelbar nach der erfolgreichen Initialisierung der Karte mit den richtigen Kartenantworten angezeigt.

Zur Initialisierung verwende ich: |CMD0|CMD8|CMD58|repeated CMD55+ACMD41|CMD58|. CMD16 verwende ich nicht...

Was kann ich tun, um das zu lösen? Hatte hier schon jemand dieses Problem? Grüße.

if (SD_initialized == true) 
{
    write_reply = SD_write_sector(1000);
    DelayMs(1000);
    write_reply = SD_write_sector(1300);
}

Sektor 1000 Sektor 1300

uint8_t SD_command[6];
uint8_t SD_write_buff[512];
uint8_t SD_read_buff[512];

uint8_t n = 0;

for (i=0; i<512; i++) 
{
    SD_write_buff[i] = n;
    n++;
}

if (SD_initialized == true) 
{
    write_reply = SD_write_sector(1000);
    DelayMs(1000);
    write_reply = SD_write_sector(1300);
}

uint8_t SPI_write (uint8_t byte) 
{
    while(SPI1STATbits.SPITBF);
    SPI1BUF = byte;
    while(!SPI1STATbits.SPIRBF);
    return SPI1BUF;
}

uint8_t SD_write_sector (uint32_t sector)   //sector = block number
{
    uint8_t reply;
    uint32_t i;

    do
    {
        reply = SD_send_cmd (24, sector); 
    }
    while (reply != 0); 

    SPI_write(0xFF);                      //Dummy byte
    SPI_write(0xFE);                      //Data start token (0xFE)

    for(i=0; i<512; i++)                  //Data block
    {
        SPI_write(SD_write_buff[i]);
    }

    SPI_write(0xFF);                      //2 bytes of CRC
    SPI_write(0xFF);                   

    reply = SPI_write(0xFF);
    reply &= 0x1F;     // Response of SD: xxx0sss1
                       // sss = 010 ---> Data accepted = 0x05
                       // sss = 101 ---> Data rejected due to CRC error = 0x0B
                       // sss = 110 ---> Data rejected due to writing error = 0x0D

    return reply;
}

uint8_t SD_send_cmd (uint8_t CMD, uint32_t ARG)
{
    SD_command[0] = CMD + 0x40;                 
    SD_command[1] = ARG >> 24;                 
    SD_command[2] = ARG >> 16;                  
    SD_command[3] = ARG >> 8;                   
    SD_command[4] = ARG & 0xFF;                
    SD_command[5] = getCRC7 (SD_command, 5);    

    SPI_write (SD_command[0]);
    SPI_write (SD_command[1]);
    SPI_write (SD_command[2]);
    SPI_write (SD_command[3]);
    SPI_write (SD_command[4]);
    SPI_write (SD_command[5]);

    SD_reply1 = SPI_write(0xFF);                //dummy read
    SD_reply1 = SPI_write(0xFF);                //actual read

    if (CMD == 8 || CMD == 58) 
    {
        SD_reply2 = SPI_write(0xFF);
        SD_reply3 = SPI_write(0xFF);
        SD_reply4 = SPI_write(0xFF);
        SD_reply5 = SPI_write(0xFF);
    }

    return SD_reply1;
}

uint8_t SD_read_sector (uint32_t sector)
{
    uint8_t reply;
    uint32_t i;

    reply = SD_send_cmd (17, sector);
    if (reply != 0) { return reply; }

    while (SPI_write(0xFF) != 0xFE);  //SPI return read byte

    for (i=0;i<512;i++) 
    { 
        SD_read_buff[i] = SPI_write(0xFF);
    }

    SPI_write(0xFF);        //read 16-bit CRC
    SPI_write(0xFF);

    return reply;
}
Es sieht so aus, als ob Ihr SD_write_sector eine Antwort zurückgibt. Was erhalten Sie in beiden Fällen für Ihre write_reply-Werte? Aus Ihren Kommentaren (da ich mit PIC32MX nicht vertraut bin) sollten beide als 0x05 zurückgegeben werden. Ist das wahr?
Was meinen Sie auch mit "Wenn ich alle Aufrufe von write_sector kommentiere, treten die Messwerte korrekt auf." Wären die Messwerte nicht alle Nullen? Was testest du zu diesem Zeitpunkt?
Möglicherweise müssen Sie eine Abfrage durchführen, bis die Karte nicht mehr belegt ist. Behobene Verzögerungen funktionieren in einigen Fällen, aber bei meinen Tests stellte ich fest, dass es notwendig war, seltsame Kartenfehler zu umgehen. Siehe _sd_command() hier: github.com/edeca/Electronics/blob/master/Include/sd_spi.c
Sean M: write_reply ist 0x05 beim ersten Aufruf von write_sector und 0x1F beim zweiten Aufruf. 0x1F, weil ich bei SD_write_sector() (Antwort &= 0x1F;) habe. Wenn ich diese Zeile kommentiere, ist die Antwort 0xFF.
@Sean M Wenn ich SD_write_sector() nicht einmal aufrufe und versuche, einen Sektor zu lesen (das heißt, wenn die Firmware nur Lesungen von Sektoren hat), sind die Lesungen in Ordnung und ich bekomme den richtigen Inhalt, der aus dem Sektor gelesen wird, wenn ich Rufen Sie SD_write_sector() mindestens einmal in der Firmware auf (nach der Karteninitialisierung), die Sektorablesungen nach diesem Schreiben geben alle 0 für denselben Sektor zurück, in dem ich die richtige Antwort (von 512 Bytes) erhalte, wenn ich nicht einmal SD_write_sector (aufrufe) ).

Antworten (2)

Um das Problem zu lösen, habe ich einfach die folgende Zeile am Ende von hinzugefügt SD_write_sector():

while (!SPI_write(0xFF));
ODER
while (SPI_write(0xFF) == 0);

Jetzt funktioniert alles korrekt (schreibt und liest).

uint8_t SD_write_sector (uint32_t sector)   //sector = block number
{
    uint8_t reply;
    uint32_t i;

    do
    {
        reply = SD_send_cmd (24, sector); 
    }
    while (reply != 0); 

    SPI_write(0xFF);                      //Dummy byte
    SPI_write(0xFE);                      //Data start token (0xFE)

    for(i=0; i<512; i++)                  //Data block
    {
        SPI_write(SD_write_buff[i]);
    }

    SPI_write(0xFF);                      //2 bytes of CRC
    SPI_write(0xFF);                   

    reply = SPI_write(0xFF);
    reply &= 0x1F;     // Response of SD: xxx0sss1
                       // sss = 010 ---> Data accepted = 0x05
                       // sss = 101 ---> Data rejected due to CRC error = 0x0B
                       // sss = 110 ---> Data rejected due to writing error = 0x0D

    **while (!SPI_write(0xFF));** <-----------------------------------
    OR
    **while (SPI_write(0xFF) == 0);**

    return reply;
}

Die Ausführung dieser Zeile dauert mit meiner 8-GB-SanDisk-microSD-Karte etwa 500 us und mit meiner 4-GB-Kingston-Karte 2,5 ms. Ich habe einen IO-Pin verwendet, um diese Zeiten zu messen.

Sie sollten beispielsweise ein Timeout implementieren timeout = 50; do { .. } while (.. whatever .. & timeout--).

Nicht sicher, was davon erwartet wird:

do
{
    reply = SD_send_cmd (24, sector); 
}
while (reply != 0);

Sie senden also CMD24 in Schleife? Sie müssen es einmal senden und auf die Antwort warten und die Antwort richtig auswerten, um zu sehen, warum die Karte nicht 0x00 zurückgegeben hat.

Außerdem müssen Sie warten, bis das Byte mit Bit 7 ungleich Null ankommt, SD_send_cmdanstatt nur zwei Bytes zu lesen und das letzte zurückzugeben. Natürlich ist eine Zeitüberschreitung erforderlich, um sicherzustellen, dass die Schleife nicht hängen bleibt, wenn die Karte nicht antwortet, aber diese Zeitüberschreitung muss ziemlich lang sein.

Was hier passiert, ist, dass Sie CMD24 in einer Schleife senden, die die Antwort nicht richtig abfängt, dann ein weiteres CMD24 usw. senden, aber der Schreibbefehl kann einer inaktiven Chipauswahl widerstehen, wie ich weiß, sodass der Schreibbefehl während dieser falschen Schleife in Bearbeitung zu sein scheint.