1
\$\begingroup\$

I am doing, for the first time, the interface between a microcontroller (PIC32MX) and a microSD card through SPI. The cards I'm using are initializing correctly. The sector/block size is fixed at 512 bytes / sector because I will only use SDHC and SDXC cards. I'm doing raw write/read on the card, because for my application I do not need a file system, it's a simple data logger. I can check the content of the card sectors with the "Hex Workshop Hex Editor" software. The SPI clock is currently at 200KHz for a starting point.

I have a function that writes an entire sector and a function that reads an entire sector. I have noticed that if I use the sector_write function with CMD24 (WRITE_BLOCK) for at least one time (and it works on the first call, writing the sector correctly), the subsequent sector_write calls does not work, I see always 0's stored at second sector that I want to write to. In this example, I'm trying to fill sector 1000 and 1300 with the same content, but only sector 1000 get filled with that content, and sector 1300 get filled with 0's (actually is its original content). Also, I've tried to write in a sequence sectors 1000 and 1001 and the result is the same (sector 1001 does not fill with the same content of sector 1000).

That is, if I call the sector_write function once, the read function returns reads from a sector full of 0 (incorrect). If I comment all the write_sector calls, the readings occur correctly.

For the following code, only sector 1000 is written ... all sector 1300 remains at 0s. This code is right after the card is successfully initialized with the correct card responses.

for initialization I use: |CMD0|CMD8|CMD58|repeated CMD55+ACMD41|CMD58|. I do not use CMD16...

What can I do to solve this? Has anyone here already faced this problem? Regards.

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

Sector 1000 Sector 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;
}
\$\endgroup\$
  • \$\begingroup\$ It looks as though your SD_write_sector returns a reply. What are you getting for your write_reply values in both cases? From your comments (as I'm not familiar with PIC32MX), they should both be returned as 0x05. Is that true? \$\endgroup\$ – Sean M Nov 8 '18 at 6:26
  • \$\begingroup\$ Also, what do you mean by "If I comment all the write_sector calls, the readings occur correctly." Wouldn't the readings be all zeros? What are you testing at that point? \$\endgroup\$ – Sean M Nov 8 '18 at 6:30
  • \$\begingroup\$ You might need to poll until the card isn’t busy. Fixed delays work in some cases, but in my testing I found it was necessary to work around strange card quirks. See _sd_command() here: github.com/edeca/Electronics/blob/master/Include/sd_spi.c \$\endgroup\$ – David Nov 8 '18 at 7:08
  • \$\begingroup\$ Sean M: write_reply is 0x05 in the first write_sector call, and 0x1F in the second call. 0x1F because I have (reply &= 0x1F;) at SD_write_sector(). If I comment that line the response is 0xFF. \$\endgroup\$ – abomin3v3l Nov 8 '18 at 11:30
  • \$\begingroup\$ @Sean M If I do not call once to SD_write_sector(), and try to read a sector (that is, when firmware has only readings of sectors), the readings are OK and I get the correct content read from the sector, If I call SD_write_sector() at least for one time in the firmware (after card init), the sector readings following this write return all 0's for the same sector where I get correct response (of 512 bytes) if I do not call once to SD_write_sector(). \$\endgroup\$ – abomin3v3l Nov 8 '18 at 11:41
2
\$\begingroup\$

To solve the problem, I've just added the following line at the end of SD_write_sector():

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

Now it's all working correctly (writes and reads).

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

This line takes around 500us to execute on with my 8GB SanDisk microSD card and 2.5ms with my 4GB Kingston card. I used an IO pin to measure those times.

\$\endgroup\$
  • \$\begingroup\$ You should implement a timeout, for example timeout = 50; do { .. } while (.. whatever .. & timeout--). \$\endgroup\$ – David Nov 8 '18 at 22:56
1
\$\begingroup\$

Not sure what is expected from this:

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

So you keep sending CMD24 in loop? You must send it once, and wait for response, evaluating response properly to see why card did not return 0x00.

Also you must wait for the byte with non-zero bit 7 to arrive in SD_send_cmd instead of just reading two bytes and returning last one. Of course timeout is required to ensure loop does not hang if card does not respond, but this timeout must be quite long.

What happens here is you sending CMD24 in loop not catching response properly, then send another CMD24 etc, but write command can withstand inactive chip select as I know so the write command appears in progress during this incorrect looping.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.