2

I am on a ZedBoard and I am trying to write on an SD card. I am working on an embedded system and I dont have any OS, I am running baremetal.

I can read fine from the SD card, their is no problem.

But when I triy to read, I have some weird behaviour :

  • f_write returns FR_OK
  • The bw (bytes written) variable is correct
  • The file is created (I can see it when I read the SD card from my PC)

But when I read the file, it is empty.

Here is my code :

void WriteFile(char const* fileName, char* buffer, size_t size)
{
    FIL file;
    FATFS fs;
    UINT bw;
    FRESULT fr;

    f_mount(&fs, "", 0);
    f_open(&file, fileName, FA_WRITE | FA_CREATE_ALWAYS);
    fr = f_write(&file, buffer, size, &bw);
    if (size != bw || fr != FR_OK)
        PRINT(("Error in writing !\n"));
    f_close(&file);
    f_mount(NULL, "", 0);
}

And I call the method like this :

 WriteFile("Hello.txt", "Hello World !", 13);

Any idea what I am doing wrong ?

Aymen
  • 83
  • 1
  • 1
  • 9
  • FatFs does not provide the low-level device I/O - you must provide that yourself of by a third party (board or chip vendor for example), so it is perhaps a problem there, though it is hard to see how if the file was created. Also FatFs can be configured as a "read-only" filesystem - but again the file was created so that seems not to be the case perhaps. In any case it seems likely that the problem is not at the level of the code you have presented, but rather at the driver of library configuration level, so as it stands you are unlikely to get a "here's your problem" answer. – Clifford Mar 27 '16 at 09:00
  • I have the same problem as you Aymen but when I try to use f_write on a text file i created from my pc I can overwrite the content of that file. But i can't get it to work with an empty file i created with f_open. – Frostbite Apr 21 '16 at 14:01
  • @Frostbite : When you say overwrite, you mean add your own data in it, and not just a blank file ? Interesting, I haven't tried this... But after reading your message, I remembered I should update the question to answered, because I found a working solution for me. – Aymen May 08 '16 at 21:14
  • I'm pretty sure that an f_sync() will do the job, as described below in https://stackoverflow.com/a/37568289/558639 – fearless_fool Jul 07 '19 at 21:13

6 Answers6

3

You can just flush the buffer (f_sync):

        FRESULT result;
        FATFS fs;
        FIL file;

        const char string[] = "Hallo world\0";
        uint16_t written = 0;
        FILINFO fno;

        /* Open or create a log file and ready to append */
        printf("mount:%d",f_mount(&fs, "", 1));
        printf("open: %d", f_open(&file, "log.txt", FA_WRITE  | FA_OPEN_ALWAYS ));
        printf("stat: %d", f_stat("log3.txt", &fno));
        printf("write: %d wr: %d", f_write(&file, string, 11, &written), written);
        printf("flush: %d", f_sync(&file));
        printf("close: %d", f_close(&file)); /* close performs sync before close */
Maarten Arits
  • 170
  • 1
  • 9
2

I found a little hack that answered my problem :
Basically it doesn't write on the SD card because the buffer isn't full yet (in the low level API of XILINX).
So what I did is pretty simple : I write the data once. And while the size of the file isn't equal or greater than the size of the data written, I write 0 (by blocks of 32)
Also, another important thing : the data you write MUST be 32 bytes aligned !\ Here is my (working) code :

size_t SizeOfFile(char const *path)
{
    FILE *fp = fopen(path, "r");
    fseek(fp, 0, SEEK_END);
    size_t fsize = (size_t) ftell(fp);
    fclose(fp);
    return fsize;
}

void WriteFile( char const* fileName, char* buffer, size_t size )
{
    size_t allignement = ( size + 32 - 1 ) & ~ ( 32 - 1 );  // We must allign by 32
    char* Buffer_logger = pleb_malloc( allignement );
    pleb_memset( Buffer_logger, 0, allignement );
    pleb_memcpy( Buffer_logger, buffer, size );

    unsigned int BytesWr;
    size_t accum = 0;
    result = f_open( &file, fileName, FA_CREATE_ALWAYS | FA_WRITE | FA_READ );
    if ( result != 0 )
    {
        return;
    }

    sprintf( Buffer_logger, "%s", buffer );

    while ( SizeOfFile( fileName ) == 0 )
    {
        // Open log for writing
        result = f_open( &file, fileName, FA_WRITE );
        if ( result != 0 )
        {
            return;
        }

        // Point to the EOF
        result = f_lseek( &file, accum );
        if ( result != 0 )
        {
            return;
        }

        // Write to log
        result = f_write( &file, (const void*) Buffer_logger, size, &BytesWr );
        if ( result != 0 )
        {
            return;
        }

        accum += accum + strlen( Buffer_logger );

        //Close file.
        result = f_close( &file );
        if ( result != 0 )
        {
            return;
        }

        pleb_memset( Buffer_logger, 0, allignement );

        size = 32;
        pleb_memset( Buffer_logger, 0, size );
    }
    pleb_free( Buffer_logger );
    PRINT( ("Data written to log Successfully\r\n") );
}
Aymen
  • 83
  • 1
  • 1
  • 9
  • 1
    An issue I had was in appending to a file with data that was written not 32 bit aligned. Whenever the write would cross a block size boundary, it either added 2 bytes or removed 2 bytes from my data. Having the data 32 bit aligned fixed the issue. This answer helped me find the issue! Thanks! – A. Blodgett Nov 03 '17 at 16:59
1

I had the same problem trying to write on a SD using a Xilinx SoC, like the ZedBoard. I was not able to write my data on a SD card, even if :

  • All functions succeed.

  • I was able to delete and create the file ( so I was not in Read-Only ).

  • When I read back what I wrote on the card (with f_lseek to go back to 0 and f_read) I read the correct data.

Each time I read the file after reboot, it was empty.

I tried the previous solutions, but none of them worked. steve's answer put me on the right track.

THE FIX: I needed to write by chunk of 256 bytes to have my data actually written to the SD. 256 bytes is the size of a page in my SD card, so it makes a lot of sense.

If the write is not a multiple of 256, it would not write all the data. Ex:

Bytes wrote using f_write -> Bytes actually written to file

512 -> 512

480 -> 256

255 -> 0

256 -> 256

Hope it helps someone :)

jaysubs
  • 21
  • 3
  • This does not really answer the question. If you have a different question, you can ask it by clicking [Ask Question](http://stackoverflow.com/questions/ask). You can also [add a bounty](http://stackoverflow.com/help/privileges/set-bounties) to draw more attention to this question once you have enough [reputation](http://stackoverflow.com/help/whats-reputation). - [From Review](/review/low-quality-posts/15910234) – madlymad Apr 22 '17 at 14:59
  • @madlymad I think OP made a typo when writing the question : ... But when I triy to **read**, I have ... by the code, he wants to write to the file and cannot do it. So I think my answer respond well to : _Any idea what I am doing wrong ?_ as for why "Hello World !" of size 13 bytes would not be written to file. Is my edited answer clearer ? – jaysubs Apr 27 '17 at 20:02
0

I had the same problem fatfs created the file but wouldnt write any data to it. No errors where produced by the fatfs functions so I was stuck for 2 days then I found that if I put a break point after the sending of the 512 byte block in the i/o layer, in my code its function send_datablock(buff, 0xFE)) it would work.

I found that after writing a data block the SD needs time to actually write this block into internal flash see

http://elm-chan.org/docs/mmc/mmc_e.html

under section Single Block Write the busy period DO line, you have to wait for this period to end before sending any other commands (ie another CMD24 single block write) else the SD discards the last CMD24 therefore you lose the data you just sent.

I put a wait loop at the end of the send_datablock function like this

    /* if not accepted, return with error */
    if((data & 0x1F) != 0x05)
    {
        return 0;
    }
    else
    {
        /* wait for SD to finish writing data */
        tmr = 5000;
        do
        {
            data = get_response(SDEXIChannel);
            if(data != 0x00)
            {
                break;
            }
            udelay(100);
            tmr--;
        } while(0 != tmr);

        /* did we time out ? if so return with error */
        if(tmr == 0)
        {
            return 0;
        }

which appears to have fixed my issue and I can now write to files on the SD card, anyway hope that saves someone two days of debugging !

steve
  • 1
0

I know this is an old post but in case someone has this problem, I was able to fix it by adding f_sync(&myFile);

            char myData[] = "Hello from STM32";
            if(f_write(&myFile, myData, strlen(myData), &myBytes) == FR_OK)
            {
                if(f_sync(&myFile) == FR_OK){
                    HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_RESET);
                    HAL_Delay(500);
                    HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_SET);
                }
            }
0

This is from a Nordic NRF52 devkit, but I was having exactly your symptoms because every single one of my f_write calls were happening inside an interrupt or nordic event notification, which I guess was not allowed.

My fix: I made a WriteFromInterrupt() function to be called from the interrupts and callbacks, which put the write into a queue, and then had another call from my main() loop that emptied the queue by finally calling f_write and f_sync.

ArtHare
  • 1,798
  • 20
  • 22