SD Card

  1. General Description
    1. Physical Sizes
    2. Speed Classes
    3. Storage Capacities
    4. File Systems
  2. SD Card API Overview
    1. Preparation
  3. Accessing the SD Card
    1. API Reference
    2. Outline
    3. Initializing the SD Card
  4. Reading and Writing of Files
    1. API Reference
    2. Searching for Files
    3. Deleting and Creating Files
    4. Writing and Reading of Files
  5. Full Code Example
  6. Appendix
    1. XDK Console Output example

General Description

The Secure Digital (SD) card is a non-volatile memory card format, which is used as a data storage unit similar to USB sticks or external hard drives. Invented in 1999 by the companies SanDisk, Panasonic and Toshiba, the SD card standard has been promoted by the SD Association (SDA). Initially designed to tackle the MultiMediaCards (MMC) format in the market, the SD card format is now used broadly in digital cameras, personal computers, embedded systems and smart devices. The evolution of the SD card standard can be summarized by the following milestones:

  • Version 1.0 with SD Standard Capacity (SDSD)
  • Version 2.0 with SD High Capacity (SDHC)
  • Version 3.0 with SD extended Capacity (SDXC)
  • Version 4.0 and 5.0 each with increasing speed classes

There are several attributes that define each and every SD card, such as the physical size, speed class, storage capacities and the formatted file system. This chapter will give a general introduction to the SD card characteristics mentioned above to help understand the basics when working with SD cards during the development of XDK projects, which utilize this feature.

Physical Sizes

There are three standard sizes for SD cards: full-size, mini- and micro-SD cards. The picture below shows the various SD card specifics in regards to standardized sizes. The size should be selected, depending on the SD card slot of the respective device. In one slot, only one specific size-type of SD cards can be inserted. However there are adapters available that allow plugging a smaller SD card into a larger SD card’s form and fit it into the given slot. Currently, the full- and micro-size SD cards are most broadly used.

Image

Speed Classes

Secondly, SD cards distinguished by their minimum sequential write speed, while their reading speed is usually similar to each other, and at least as fast as the write speed. There are three speed class categories: SD speed, Ultra-High-Speed (UHS) and the Video speed class. The speed classes and their respective minimum sequential write speeds are listed in the following table.

Speed Classes and their Write Speed

Write SpeedSD Speed ClassUHS Speed ClassVideo Speed Class
2 MB/sClass 2 (C2)  
4 MB/sClass 4 (C4)  
6 MB/2Class 6 (C6)Class 6 (V6) 
10 MB/sClass 10 (C10)Class 1 (U1)Class 10 (V10)
30 MB/s Class 3 (U3)Class 30 (V30)
60 MB/s  Class 60 (V60)
90 MB/s  Class 90 (V90)

Storage Capacities

The storage capacity of the card is determined by three factors. The first factor is the SD card generation, and more precisely, the specification version the SD card is based on. The SD card generations SDSD, SDHC, and SDXC have the following maximum storage capacities:

  • SDSD up to 2GB
  • SDHC up to 32GB
  • SDXC up to 2TB

Secondly the storage capacity is defined by the manufacturer that can offer a wide variety of capacities in their respective pricing categories to customers with different use cases for the SD card.

The last factor is the file system used on the SD card. This can limit the storage capacity of the SD card, even though the maximum capacity provided by the manufacturer is greater, because of the constraints of the respective file system (e.g. FAT16, FAT32, exFAT).

File Systems

There are four file system standards for SD cards which are all based on the File Allocation Table (FAT) concept, where each and every file block is referenced in a list of entries. This list is called the file allocation table. Although FAT file systems have been very common for Microsoft Windows operating systems, they have been replaced by more sophisticated ones, such as the New Technology File System (NTFS) supporting features like journaling where each write access is protocoled, which allows the operating system to restore a consistent state for data, even if a write access has been aborted due to a system crash or a power outage.

For each SD card generation the SDA defines specific FAT variations:

  • FAT12 / FAT16 for SDSD up to 2GB
  • FAT32 for SDHC from 2GB to 32GB
  • exFAT for SDXC from 32GB to 2TB

The development of the exFAT standard for the portable memory market was mainly due to a limitation of FAT32 systems, which only allows for maximum file sizes of 4GB. Files sizes that great are often required, given that SD cards are applied in digital cameras and other mobile devices, where huge amounts of data can be generated (e.g. 4K video files, log files over a long period of time)

SD Card API Overview

As with most XDK functionalities, there are also APIs provided in the SDK that allow simple access to necessary functions for working with SD cards. This makes it easier for developers to get started, without requiring attention to specific details on a deeper level.

There are two libraries that are used within this SD card guide:

  • XDK Driver API for managing the SD card (e.g. initialization, status)
  • 3rd Party API for FAT file system operations on the card (e.g. mounting the card, creating and deleting files)

Image

The combination of both libraries in the XDK application for this guide allows for implementing any use case which requires accessing SD cards, including the creation, reading, writing and modification of files on SD cards.

Preparation

The application in this guide is based on an empty XdkApplicationTemplate project, which can be opened from the Welcome-screen of the XDK-Workbench. The code snippets have to be placed above AppController_Init() one after another. As a guideline, the order of the code snippets can be taken over from this guide. Using a different order might lead to compilation errors during the build process.

This guide explains and uses functions from the two main libraries for manipulating files on the SD Card. The first is the library BCDS_SDCard_Driver.h, which offers functions for initializing and accessing the SD card module. The other one is the library ff.h, which offers functions for accessing and manipulating files on the SD card. Additionally, in this guide some constant values will be used. For better readability, they will be represented by macros, which are created using #define statements. The following code shows how to include the libraries and create the macros used in this guide. The code should be placed near the top of your implementation file.

#include "BCDS_SDCard_Driver.h"
#include "ff.h"

// constant definitions
#define DEFAULT_LOGICAL_DRIVE   ""
#define DRIVE_ZERO              UINT8_C(0)
#define FORCE_MOUNT             UINT8_C(1)
#define FIRST_LOCATION          UINT8_C(0)

As explained in chapter 1, there are several characteristics that have to be considered while selecting the correct SD card for the XDK project. Which SD cards are supported by the XDK can be read up in the FAQ section of the XDK homepage:

  1. What size SD card does it support?

    XDK supports Micro SD cards up to version 2.0 of the SD specification (card capacity up to 32 GB) with FAT (File Allocation Table) filesystems. Note: SDXC is NOT supported! We recommend anything up from Class 4 performance wise.

This means that any micro SD card fulfilling the SD specification up to 2.0 (including SDSD and SDHC) is supported, but SDXC is not. Therefore it is important to note that the limitations of FAT32 apply to any XDK project that includes a SD Card:

  • maximum capacity 32GB
  • maximum file size 4GB

Depending on the use case of the XDK project, the SD card capacity, and the needed speed class may be of greater importance, but for this example guide these characteristics are not relevant, as this guide is intended for learning the basics.

Finally, the SD card needs to be formatted to a FAT filesystem. It is recommended to use the FAT32 file system in order to be more flexible regarding the maximum capacities and file sizes of SD cards. To achieve this, you can use a micro SD card reader or a full-size card reader with an adapter that will allow you to place the micro SD card into a full-size SD card. Note: When formatting an SD card, all data, which has been previously stored on the card, will be lost.

Now that the SD card is ready, you can insert it into the SD card slot of the XDK.

Accessing the SD Card

API Reference

In general, it is recommended to develop an application based on the highest API level the XDK framework supports. The high level API for SD card control is documented in the header file BCDS_SDCard_Driver.h.

Additionally, you can find a list of the main functions we will be using for the implementation of accessing a SD card below. The table below shows the SD card API functions we will use. For a full description, please refer to the corresponding API.

FunctionDescription
SDCardDriver_Initialize()This function is called to initialize the SD card module in the XDK while configuring the hardware, registering callback functions for SD card events and setting up the communication path to the SD card module (SPI)
SDCardDriver_GetDetectStatus()This function is used to get the status of the SD card whether the SD card has been inserted or removed
SDCardDriver_DiskInitialize()This function is called to initialize the communication to the inserted SD card in the SD card module of the XDK and to verify the card status
SDCardDriver_Deinitialize()This function is not used in this guide, but it can be helpful in XDK projects with SD cards where the system needs to run on a battery. Then the de-initialization of the SD card into idle state can reduce the overall energy consumption by some mA depending on the SD card

Outline

The following code shows the general outline of what needs to be implemented. It also shows the implementation of our AppControllerEnable() function. This function is generally used as the function after the entry point AppController_Init() and the setup function AppControllerSetup() to every XDK application. In this guide it calls all the functions we define in our example project that are applying the SD card and FAT file system API functions in the XDK.

static void AppControllerEnable(void * param1, uint32_t param2)
{
    BCDS_UNUSED(param1);
    BCDS_UNUSED(param2);

    const char Filename[] = "test_xdk.txt";
    const char FileContent[] = "Hello XDK community, the SD card guide created content on this SD card \n";

    if(RETCODE_OK == searchForFileOnSdCard(Filename, NULL)){
        deleteFileOnSdCard(Filename);
    }

    createFileOnSdCard(Filename);

    writeDataIntoFileOnSdCard(Filename, FileContent);
    readDataFromFileOnSdCard(Filename);
}

The first function that is not part of the empty example project provided by the XDK SDK is the InitSdCard() function, that is described in detail in the following chapter Initializing the SD Card. The rest of the code shows the next steps after initializing the SD card and will be explained in detail in the following chapter Writing and Reading of Files, in which the handling of files is tackled.

Initializing the SD Card

To make the SD Card available for handling, four steps are performed in this guide using the API:

  • Initialize the SD card module of the XDK
  • Check if the SD card is inserted
  • Initialize and verify the currently inserted SD card
  • Mount the SD card

The above described three steps can be seen in the code below, which shows the full implementation of the InitSdCard() function. After preparing variables for the return values of the functions, the first method from the SD card API that is used is SDCardDriver_Initialize(). As described in the API Reference, calling this functions leads to an initialization of all the components and interfaces that are needed to run the SD card module into which the SD card is inserted.

The above described three steps can be seen in the code below, which shows the full implementation of the InitSdCard() function. After preparing the variables for the return values of the subsequent functions, the first function from the SD card API that is used is SDCardDriver_Initialize(). As described in chapter Accessing the SD Card, calling this functions initializes all the components and interfaces that are required to run the SD card module, into which the SD card is inserted.

After the module is initialized, it is recommended to first detect whether an SD card is inserted or not. This can be achieved by calling the function SDCardDriver_GetDetectStatus().

If this function’s return value shows that an SD card was inserted, the function SDCardDriver_DiskInitialize() is called. As input for this function the argument DRIVE_ZERO (this resolves to 0) is used, which defines the SD card physical drive location (0 is the default for the XDK).

void InitSdCard(void){
  Retcode_T retVal = RETCODE_FAILURE;
  FRESULT FileSystemResult = FR_OK;
  static FATFS FatFileSystemObject;
  SDCardDriver_Initialize();
  if(SDCARD_INSERTED == SDCardDriver_GetDetectStatus()){
    retVal = SDCardDriver_DiskInitialize(DRIVE_ZERO);
    if(RETCODE_OK == retVal){
      printf("SD Card Disk initialize succeeded \n\r");
      FileSystemResult = f_mount(&FatFileSystemObject, DEFAULT_LOGICAL_DRIVE,
                                    FORCE_MOUNT);
      if (FR_OK != FileSystemResult){
        printf("Mounting SD card failed \n\r");
      }
    }
  }
}

If all three initialization steps are successful, the SD card file system can be safely mounted with a function call from the FAT file system API. The SD card file system has to be mounted before it can be finally be accessed and data written to or read from it.

The file system will be represented as the static variable FatFileSystemObject of type FATFS in the code. A reference to this variable will be stored internally in the Fat Filesystem Module, which is why this variable must persist throughout the complete runtime of the application. That is why this variable is declared static, so it won’t be deleted after the function InitSdCard() is finished. Additionally, the path for the logical drive is passed by DEFAULT_LOGICAL_DRIVE (this resolves to an empty string) and the option to force the mounting process immediately by passing FORCE_MOUNT (this resolves to 1).

Reading and Writing of Files

API Reference

Similar to the SD card API, it is also recommended for the file access chapter to use the highest API level the XDK framework supports. The high level API for file access is described in the header file ff.h.

In addition to that, the main functions, which will be used in this guide’s example project, are described in the table below and in the following chapters. The table below shows the file API methods we will use. For a full description, please refer to the corresponding API.

FunctionDescription
f_mount()This function is called to mount the SD card as a logical drive with the desired file system (e.g. FAT) at the logical drive location. It is also possible to use this function for unmounting the previously mounted logical drive by using NULL as an argument instead of the file system.
f_stat()This function is called to check the existence of a file at the given file path and returns status information about the file, such as the file size, timestamp and attributes (e.g. read-only).
f_open()This function is called to create a new file or open an existing one given by the file path and to make it available for further content manipulation as a file object. Additional behavior can be specified with the file mode (e.g. append content to existing file or overwrite existing content).
f_unlink()This function is called to remove a file or sub-directory at the given path.
f_lseek()This function is called to move the write / read pointer inside of the given file object to the desired position (e.g. beginning or end of file).
f_size()This function is called to return the file size of the given file object.
f_write()This function is called to write into the given file object. The inputs are the data to be written (e.g. as a pointer to buffer), number of bytes to be written and a pointer to store the actual number of bytes written after execution. The write / read pointer of the file object advances the number of bytes written.
f_close()This function is called to close the given file object in order to finish the file manipulations done to the respective file on the file system. It is necessary to finish any file access with this function to avoid data loss on the file system (high probability with FAT).
f_read()This function is called to read data from the given file object with the same arguments as for the write function, with the difference that the data buffer is used to store the data that was read. The write / read pointer of the file object advances the number of bytes written.

Searching for Files

As can be seen in the Outline Code from chapter Initializing the SD Card, the next step in this guide, after initializing the SD card and mounting it as a logical drive, is to search for existing files (for example "test_xdk.txt" as seen in the function AppControllerEnable() with the variable Filename) in the root directory, using the function searchForFileOnSdCard().

This is done in the implementation of the searchForFileOnSdCard() function, which is shown in the code below, by using f_stat() with the given string in filename. For the sake of simplicity in this guide, the file information fileData, which is an output parameter of the function, is not used here. To see what while information can be accessed, see the definition of the FILINFO data type

Retcode_T searchForFileOnSdCard(const char* filename, FILINFO* fileData){
  if(FR_OK == f_stat(filename, fileData)){
    printf("File %s found on SD card. \n\r",filename);
    return RETCODE_OK;
  } else {
    printf("File %s does not exist. \n\r",filename);
    return RETCODE_FAILURE;
  }
}

While the search function searchForFileOnSdCard() only checks the top level directory for the given file, more advanced multi-level search functions can be implemented by using file system API functions such as f_opendir() and f_readdir() which are described in the library ff.h.

Deleting and Creating Files

Given that the case that file with the name in the variable Filename already exists on the SD card, it may be useful to delete before writing content to a fresh version of that file, as it is done in the Outline Code from chapter Initializing the SD Card. A file can be deleted using the function implementation of deleteFileOnSdCard(), which is shown in the following code.

void deleteFileOnSdCard(const char* filename){
  f_unlink(filename);
}

The deleteFileOnSdCard() function utilizes the f_unlink() function of the filesystem API and passes filename as an argument, which contains the name of the file that will be deleted.

After deleting the file the, next step seen in the Outline Code from chapter Initializing the SD Card is to create a new file with the same file name in the top level directory. This is implemented in the function createFileOnSdCard(), which can be seen in the following code.

void createFileOnSdCard(const char* filename){
  if(FR_OK == f_open(&fileObject, filename, FA_CREATE_NEW)){
    printf("File %s was created successfully \n\r",filename);
  }
}

To create a file, the createFileOnSdCard() function calls the filesystem API function f_open() with a pointer to a file object. The file object will represent the file on the SD card in the code and can then be used for any further actions on this file. In order to have access to this file object from every function in our example project, it has to be declared as a global variable at the top of source code, preferably just below the include statements and define macros (see chapter Preparation). The code below shows how this can be done.

// file object pointer to be used for all file actions
static FIL fileObject;

The other parameters used by the f_open() function are the file name stored in the variable filename, and the file access mode. In this case, FA_CREATE_NEW is used, which will force the filesystem to create a new file.

Writing and Reading of Files

Now that the file is opened and reachable via the global file object pointer fileObject, the file’s content can be written to or read.

Every file access, writing or reading, needs to be performed in a specific sequence of:

  • opening the file with the correct file mode
  • setting the write / read file pointer to the desired position (for example the beginning of file)
  • writing data to the file / reading data from the file
  • closing the file to avoid data corruption or data loss

For writing data into a file on the SD card, the sequence described above is implemented in the function writeDataIntoFileOnSdCard() as shown in the code below, using various filesystem API functions. To open the file defined by the file name in filename and load it into the global file object pointer fileObject, the function f_open() is used. Additionally the file mode is defined by FA_OPEN_EXISTING | FA_WRITE which means that either the existing file with the given filename will be opened, or a new one will be created and subsequently opened for writing.

Repositioning the write pointer of the file object pointer fileObject can be done by using f_lseek(). With the help of the function f_size() which returns the file size, the pointer can be directly positioned to the end of the file.

Now that the write pointer is set, the write process can be started by calling the file API function f_write(). This function accesses the global file object pointer, writes the content of the data buffer ramBufferWrite, which contains the data from the input parameter dataBuffer. Since the function writeDataIntoFileOnSdCard() is called within appInitSystem() from the Outline Code from chapter Initializing the SD Card, feel free to place any text into the variable FileContent. In addition to that, the size of the length of the data is passed to the write function as the variable fileSize. Finally, a pointer to the variable bytesWritten is used in the function to store the actual number of bytes that were written to the file in this variable.

The last step of the writing procedure is done by calling the closing function f_close() from the filesystem API with the given global file object pointer in fileObject, in order to finish the file access properly and avoid data being corrupted or even lost.

void writeDataIntoFileOnSdCard(const char* filename, const char* dataBuffer){
  FRESULT fileSystemResult;
  char ramBufferWrite[UINT16_C(512)]; // Temporary buffer for write file
  uint16_t fileSize;
  UINT bytesWritten;
  fileSize = (uint16_t) strlen(dataBuffer);

  for(uint32_t index = 0; index < fileSize; index++){
    ramBufferWrite[index] = dataBuffer[index];
  }
  f_open(&fileObject, filename, FA_OPEN_EXISTING | FA_WRITE);
  f_lseek(&fileObject, f_size(&fileObject));
  fileSystemResult = f_write(&fileObject, ramBufferWrite, fileSize,
  &bytesWritten);

  if((fileSystemResult != FR_OK) || (fileSize != bytesWritten)){
    printf(" Error: Cannot write to file %s \n\r",filename);
  }
  fileSystemResult = f_close(&fileObject);
}

Note: While the function writeDataIntoFileOnSdCard() describes how to write data in ASCII format into the file there is also the possibility to write raw data blocks into the file with the function SDCard_Driver_DiskWrite() from the library BCDS_SDCard_Driver.h. Writing data block wise is considered to be more performant but brings with it the disadvantage that the file may not be humanly readable.

Now that some content was successfully written into a file, the last step of this guide’s example project is to read the written content from that file and print it out in order to check if everything was executed as expected.

Reading is implemented by the function readDataFromFileOnSdCard(), which is described in the code below and is called by the AppControllerEnable() function in the Outline Code from chapter Initializing the SD Card with the file name passed as an argument in Filename.

Reading is done in the same fashion as writing, following the same steps of opening, setting the read pointer, reading data and closing the file. The only major difference here is that the file is first searched on the SD card in order to avoid invalid file actions on a non-existing file within the functionsearchForFileOnSdCard(). After opening the file with f_open(), see the writing section above for details about the arguments, the read pointer is set to the beginning of the file with FIRST_LOCATION in order to read all the data inside of the file.

With f_read() the file content, addressed through the global file object pointer fileObject, is read into the buffer ramBufferRead. The number of bytes to be read is extracted from the fileInfo structure as received from the searchForFileOnSdCard() function. This effectively reads the entire file. The resulting number of bytes read from the file into the buffer are written to the variable bytesRead, making it possible to check whether the attempted number of bytes to be read is matching the number of bytes that were actually read. If the check is valid, the NULL element ('\0') is added at the end of the content placed into the buffer ramBufferRead, signaling string-related functions that they reached the end of the string (as defined the C language).

After printing out the successfully read file content, it is important to close the global file object with the file API function f_close(), since there are no more file actions to be done in this function.

// Insert this code beneath the implementation of searchForFileOnSdCard()
  void readDataFromFileOnSdCard(const char* filename){
  FRESULT fileSystemResult;
  FILINFO fileInfo;
  char ramBufferRead[UINT16_C(512)]; // Temporary buffer for read file
  UINT bytesRead;
  if(RETCODE_OK == searchForFileOnSdCard(filename,&fileInfo)){
    f_open(&fileObject, filename, FA_OPEN_EXISTING | FA_READ);
    f_lseek(&fileObject, FIRST_LOCATION);
    fileSystemResult = f_read(&fileObject, ramBufferRead, fileInfo.fsize,
                                  &bytesRead);
    if((fileSystemResult != FR_OK) || (fileInfo.fsize != bytesRead)){
      printf("Error: Cannot read file %s \n\r",filename);
    }
    else{
      ramBufferRead[bytesRead] = '\0';
      printf("Read data from file %s of the SD card \n\r",filename);
      printf(ramBufferRead);
      printf("\n\r");
    }
    f_close(&fileObject);
  }
  else{
    printf("No file with name %s exists on the SD card \n\r",filename);
  }
}

Note: While the function readDataFromFileOnSdCard() describes how to read data in ASCII format into the file there is also the possibility to read raw data blocks from the file with the function SDCard_Driver_Diskread() from the library BCDS_SDCard_Driver.h. Reading data block-wise is considered to be more performant but requires that the data was written into the file block wise as well.

Full Code Example

Note: The full code example is intended for XDK-Workbench versions 3.4.0 and higher.

/* --------------------------------------------------------------------------- |
 * INCLUDES & DEFINES ******************************************************** |
 * -------------------------------------------------------------------------- */

/* own header files */
#include "XdkAppInfo.h"
#undef BCDS_MODULE_ID  /* Module ID define before including Basics package*/
#define BCDS_MODULE_ID XDK_APP_MODULE_ID_APP_CONTROLLER

/* system header files */
#include <stdio.h>

/* additional interface header files */
#include "BCDS_CmdProcessor.h"
#include "FreeRTOS.h"
#include "BCDS_SDCard_Driver.h"
#include "ff.h"

/* --------------------------------------------------------------------------- |
 * HANDLES ******************************************************************* |
 * -------------------------------------------------------------------------- */

static CmdProcessor_T * AppCmdProcessor;/**< Handle to store the main Command processor handle to be used by run-time event driven threads */

/* --------------------------------------------------------------------------- |
 * VARIABLES ***************************************************************** |
 * -------------------------------------------------------------------------- */

#define DEFAULT_LOGICAL_DRIVE   ""
#define DRIVE_ZERO              UINT8_C(0)
#define FORCE_MOUNT             UINT8_C(1)
#define FIRST_LOCATION          UINT8_C(0)

static FIL fileObject;

/* --------------------------------------------------------------------------- |
 * EXECUTING FUNCTIONS ******************************************************* |
 * -------------------------------------------------------------------------- */

void InitSdCard(void){
  Retcode_T retVal = RETCODE_FAILURE;
  FRESULT FileSystemResult = FR_OK;
  static FATFS FatFileSystemObject;
  SDCardDriver_Initialize();
  if(SDCARD_INSERTED == SDCardDriver_GetDetectStatus()){
    retVal = SDCardDriver_DiskInitialize(DRIVE_ZERO);
    if(RETCODE_OK == retVal){
      printf("SD Card Disk initialize succeeded \n\r");
      FileSystemResult = f_mount(&FatFileSystemObject, DEFAULT_LOGICAL_DRIVE,
                                    FORCE_MOUNT);
      if (FR_OK != FileSystemResult){
        printf("Mounting SD card failed \n\r");
      }
    }
  }
}

Retcode_T searchForFileOnSdCard(const char* filename, FILINFO* fileData){
  if(FR_OK == f_stat(filename, fileData)){
    printf("File %s found on SD card. \n\r",filename);
    return RETCODE_OK;
  } else {
    printf("File %s does not exist. \n\r",filename);
    return RETCODE_FAILURE;
  }
}

void deleteFileOnSdCard(const char* filename){
  f_unlink(filename);
}

void createFileOnSdCard(const char* filename){
  if(FR_OK == f_open(&fileObject, filename, FA_CREATE_NEW)){
    printf("File %s was created successfully \n\r",filename);
  }
}

void writeDataIntoFileOnSdCard(const char* filename, const char* dataBuffer){
    FRESULT fileSystemResult;
    char ramBufferWrite[UINT16_C(512)]; // Temporay buffer for write file
    uint16_t fileSize;
    UINT bytesWritten;
    fileSize = (uint16_t) strlen(dataBuffer);

    for(uint32_t index = 0; index < fileSize; index++){
        ramBufferWrite[index] = dataBuffer[index];
    }
    f_open(&fileObject, filename, FA_OPEN_EXISTING | FA_WRITE);
    f_lseek(&fileObject, f_size(&fileObject));
    fileSystemResult = f_write(&fileObject, ramBufferWrite, fileSize, &bytesWritten);

    if((fileSystemResult != FR_OK) || (fileSize != bytesWritten)){
        printf("Error: Cannot write to file %s \n\r", filename);
    }
    fileSystemResult = f_close(&fileObject);
}

void readDataFromFileOnSdCard(const char* filename){
    FRESULT fileSystemResult;
    FILINFO fileInfo;
    char ramBufferRead[UINT16_C(512)]; // Temporary buffer for read file
    UINT bytesRead;

    if(RETCODE_OK == searchForFileOnSdCard(filename, &fileInfo)){
        f_open(&fileObject, filename, FA_OPEN_EXISTING | FA_READ);
        f_lseek(&fileObject, FIRST_LOCATION);
        fileSystemResult = f_read(&fileObject, ramBufferRead, fileInfo.fsize, &bytesRead);

        if((fileSystemResult != FR_OK) || (fileInfo.fsize != bytesRead)){
            printf("Error: Cannot read file %s \n\r", filename);
        }
        else{
            ramBufferRead[bytesRead] = '\0';
            printf("Read data from file %s of the SD card \n\r", filename);
            printf(ramBufferRead);
            printf("\n\r");
        }
        f_close(&fileObject);
    }
    else{
        printf("No file with name %s exists on the SD card \n\r", filename);
    }
}

/* --------------------------------------------------------------------------- |
 * BOOTING- AND SETUP FUNCTIONS ********************************************** |
 * -------------------------------------------------------------------------- */

static void AppControllerEnable(void * param1, uint32_t param2)
{
    BCDS_UNUSED(param1);
    BCDS_UNUSED(param2);

    const char Filename[] = "test_xdk.txt";
    const char FileContent[] = "Hello XDK community, the SD card guide created content on this SD card \n";

    if(RETCODE_OK == searchForFileOnSdCard(Filename, NULL)){
        deleteFileOnSdCard(Filename);
    }

    createFileOnSdCard(Filename);

    writeDataIntoFileOnSdCard(Filename, FileContent);
    readDataFromFileOnSdCard(Filename);
}

static void AppControllerSetup(void * param1, uint32_t param2)
{
    BCDS_UNUSED(param1);
    BCDS_UNUSED(param2);
    Retcode_T retcode = RETCODE_OK;

    /* @todo - Setup the necessary modules required for the application */
    vTaskDelay(5000);

    InitSdCard();

    retcode = CmdProcessor_Enqueue(AppCmdProcessor, AppControllerEnable, NULL, UINT32_C(0));
    if (RETCODE_OK != retcode)
    {
        printf("AppControllerSetup : Failed \r\n");
        Retcode_RaiseError(retcode);
        assert(0); /* To provide LED indication for the user */
    }
}

void AppController_Init(void * cmdProcessorHandle, uint32_t param2)
{
    BCDS_UNUSED(param2);

    Retcode_T retcode = RETCODE_OK;

    if (cmdProcessorHandle == NULL)
    {
        printf("AppController_Init : Command processor handle is NULL \r\n");
        retcode = RETCODE(RETCODE_SEVERITY_ERROR, RETCODE_NULL_POINTER);
    }
    else
    {
        AppCmdProcessor = (CmdProcessor_T *) cmdProcessorHandle;
        retcode = CmdProcessor_Enqueue(AppCmdProcessor, AppControllerSetup, NULL, UINT32_C(0));
    }

    if (RETCODE_OK != retcode)
    {
        Retcode_RaiseError(retcode);
        assert(0); /* To provide LED indication for the user */
    }
}

/** ************************************************************************* */

Appendix

XDK Console Output example

The following console log is an example output of the code that has been implemented in the SD card example guide (file “test_xdk.txt” already existing):

  INFO | XDK DEVICE 1: SD Card Disk initialize succeeded
  INFO | XDK DEVICE 1: File test_xdk.txt found on SD card.
  INFO | XDK DEVICE 1: File test_xdk.txt was created successfully
  INFO | XDK DEVICE 1: File test_xdk.txt found on SD card.
  INFO | XDK DEVICE 1: Read data from file test_xdk.txt of the SD card
  INFO | XDK DEVICE 1: Hello XDK community, the SD card guide created Content on this SD Card