Electronic – MicroSD raw write/read mode. After sector write once, can’t write more sectors and read always 0’s

picsdspi

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

Best Answer

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.