Troubleshooting TF Card Write Operations on Zynq Platforms
1. Fundamental Concepts
Previous experiments with Zynq TF card read/write operations demonstrated expected behaviors through serial port outputs. However, deeper investigation into common functions within the "ff.h" header file reveals significant differences when implementing TF card operations. This document outlines frequent challenges encountered during TF card implementation on Zynq platforms, serving as a reference for future development.
2. Practical Implementation Issues
Consider the following complete code example:
#include
#include "xil_printf.h"
#include "xdevcfg.h"
#include "xparameters.h"
#include "ff.h"
int memory_card_init(void);
int sd_card_test_write(void);
int main()
{
memory_card_init();
sd_card_test_write();
while(1)
{
;
}
return 0;
}
static FATFS fs_object;
int memory_card_init()
{
FRESULT result;
result = f_mount(&fs_object, "", 0);
if(result != FR_OK)
{
return XST_FAILURE;
}
return XST_SUCCESS;
}
int sd_card_test_write()
{
FILE file_obj;
FRESULT result;
UINT bytes_written;
const char test_data[] = "t\n";
result = f_open(&file_obj, "test.txt", FA_OPEN_ALWAYS | FA_WRITE);
if(result != FR_OK)
{
return XST_FAILURE;
}
result = f_lseek(&file_obj, f_size(&file_obj));
result = f_write(&file_obj, test_data, sizeof(test_data), &bytes_written);
result = f_lseek(&file_obj, f_size(&file_obj));
result = f_write(&file_obj, "t/n", sizeof("t/n"), &bytes_written);
// Optional: result = f_sync(&file_obj);
result = f_close(&file_obj);
return result;
}
Key components of this implementation are explained below:
The
memory_card_init function utilizes
f_mount to initialize the SD card partition. This operation typically needs to be performed only once and facilitates subsequent access to the SD card's memory space. The return value verification ensures successful mounting. The
f_mount function can also be used to unmount a workspace by calling
f_mount(0, NULL), which unmounts the root directory workspace.
The
sd_card_test_write function contains several critical operations:
f_open,
f_lseek,
f_write,
f_sync,
f_close, and
f_size.
(1) f_open
This function opens or creates files. The first parameter is the address of the file object, the second is the filename string, and the third specifies the access mode.
FA_OPEN_ALWAYS: Always opens the file. Creates a new file if it doesn't exist, otherwise opens the existing file.
FA_OPEN_EXISTING: Opens only if the file exists. Fails if the file doesn't exist.
FA_CREATE_NEW: Creates a new file. Fails if the file already exists.
FA_CREATE_ALWAYS: Always creates a new file, overwriting any existing file.
FA_WRITE: Write permission mode, allowing data modification.
FA_READ: Read permission mode, allowing data access.
Note that the first four modes are operational modes, while the last two are permission modes. These can be combined as needed.
The distinction between
FA_OPEN_ALWAYS and
FA_CREATE_ALWAYS is significant: both result in a file, but
FA_OPEN_ALWAYS preserves existing content, while
FA_CREATE_ALWAYS overwrites it.
(2) f_lseek
This function sets the file operation pointer. The first parameter is the file object address, and the second can be a numerical offset or a special class representing file size. It's commonly used with
f_size to position the pointer at the end of the file for append operations.
(3) f_write
This function writes data to the file. The parameters are: file object address, data source address (string, array, etc.), data length, and a pointer to a UINT variable for counting bytes written.
A critical consideration is that multiple
f_write operations may encounter data corruption if the data blocks are too large. To mitigate this, increase the data size to multiples of 256 bytes (determined through testing). This limitation makes
f_write less efficient for SD card operations. Alternative approaches like
f_printf may offer better performance.
(4) f_sync
This function flushes cached data to the physical storage, ensuring data integrity. It takes the file object address as its parameter. While not demonstrated in this example, it's recommended for critical applications where data consistency is essential.
(5) f_close
This function closes the file and releases resources. It requires only the file object address as its parameter.
test my_sd function!
test my_sd function!
test my_sd function!
t
t
t
t/n
This sample output demonstrates the contents generated by the code above.
3. Summary
The basic steps for file writing operations can be mapped to PC file operations as follows:
Create workspace == Mount storage device
Open file == Open file (can create directories with escape characters)
Set write position == Click cursor at specific location
Write data == Input text via keyboard
Verify file == Confirm input correctness
Close file == Save and close file
Unmount workspace == Eject storage device
This parallel illustrates the fundamental workflow of file writing operations. For read operations, the principle remains similar, with
f_write replaced by
f_read. Both functions share identical parameter structures, with the primary difference being the access mode specified in
f_open. When storing data, ensure proper use of string arrays for data containment.