File System
Introduction
The File System module in NECTO IDE provides a seamless way to integrate file storage and retrieval capabilities into embedded systems, with a primary focus on SD card support utilizing the FAT
file system. This module simplifies the often complex task of managing file operations in embedded environments, allowing developers to work with files in a manner similar to desktop systems, but optimized for the limited resources typical of microcontroller platforms.
This comprehensive guide serves as a reference for developers, outlining the key features and functionalities of the MikroSDK.FileSystem API. It aims to provide clear, practical insights into how this API can be effectively used to implement robust and efficient file storage solutions on microcontroller-based systems. Whether you are working with large amounts of data or simply need reliable file management on an SD card, this guide will help you navigate the capabilities of the MikroSDK.FileSystem module to meet the storage requirements of your embedded application.
API Name
- MikroSDK.FileSystem
API Files
-
GitHub Repository:
Prerequisites
-
Library Manager:
- Select FileSystem, PhysicalDrive and Board to add the
File System
to your project using theLibrary Manager
in NECTO Studio.
- Select FileSystem, PhysicalDrive and Board to add the
-
Headers:
- Include the MikroSDK.FileSystem, MikroSDK.PhysicalDrive, MikroSDK.Board headers in your source files to access
File System
functions. - This set of headers pop up in the lower right part of NECTO Studio once you perform the previous task (while selecting FileSystem, and Board in Library Manager)
- Alternatively, include the fatfs.h, file.h, dir.h, sdspi_physical_drive.h, and ff_types.h headers in your source files to access the
FileSystem
functions.
- Include the MikroSDK.FileSystem, MikroSDK.PhysicalDrive, MikroSDK.Board headers in your source files to access
-
CMakeLists:
- You do not have to perform anything, configuration (CMake) file is already taken care of automatically while you performed the first task in this Prerequisites!
- You can ensure that your CMakeLists.txt includes the necessary configurations to link against the
FileSystem
library.
Code Examples
- SD Card File Manipulation with NECTO IDE and mikroSDK v2.0 Framework
/* Project name:
* SD Card File Manipulation with NECTO IDE and mikroSDK v2.0 Framework
* Copyright:
* (c) MIKROE, 2024.
* Description:
* - This example demonstrates how to format, create, write to, read from,
* and manipulate files on an SD card using the FAT file system in NECTO Studio.
* - The code opens a file on the SD card, writes data to it, and performs
* a file seek operation to verify correct file handling.
* - The test outcome is indicated via digital output pins to show success or failure.
* Library dependencies:
* - Ensure `FileSystem`, `PhysicalDrive`, `Log`, and `Board` libraries are enabled
* in NECTO Studio's Library Manager to ensure successful compilation and functionality.
* How to test this code example:
* - 1. Insert [mikroSD Click]https://www.mikroe.com/microsd-click with SD card
* into the appropriate socket on one of the
* [development boards](https://www.mikroe.com/development-boards-v8)
* (this example is set to utilize mikroBUS 1 socket).
* - 2. Ensure the board is powered and connected to the PC.
* - 3. Compile and flash the code to the MCU.
* - 4. Use the USB UART to monitor logs during execution (alternatively,
* utilize Debug Mode and `Application Output` for monitoring).
* - 5. Observe the status LEDs.
*/
// ------------------------------------------------------------------ INCLUDES
/**
* Any initialization code needed for MCU to function properly.
* Do not remove this line or clock might not be set correctly.
*/
#ifdef PREINIT_SUPPORTED
#include "preinit.h"
#endif
#include "fatfs.h" // File system handling library
#include "file.h" // File operations library
#include "board.h" // Board-specific definitions and configurations
#include "sdspi_physical_drive.h" // SD card handling via SPI
#include "ff_types.h" // FAT filesystem types
#include "log.h" // Logging utility
#include "delays.h" // Delay functions for timing operations
// -------------------------------------------------------------------- MACROS
#define SEEK_TEST_LENGTH (5) // Length of the buffer used for seek test
#define BUFFER_SIZE (512) // Buffer size for file read/write operations
// Define pins to indicate test status through LEDs
#define TEST_SUCCESS_PIN NC // TODO: use pin PD12 for example
#define TEST_FAILED_PIN NC // TODO: use pin PD13 for example
// ----------------------------------------------------------------- VARIABLES
digital_out_t status; // Status LED control
typedef enum {
TEST_FS_NO_ERROR = 0, // No error
TEST_FS_ERROR_MKDIR, // Error in directory creation
TEST_FS_ERROR_REMOVE, // Error in file/directory removal
TEST_FS_ERROR_RENAME, // Error in renaming file/directory
TEST_FS_ERROR_FORMAT, // Error in drive formatting
TEST_FS_ERROR_MOUNT, // Error in mounting the file system
TEST_FS_ERROR_FOPEN, // Error in opening a file
TEST_FS_ERROR_FCLOSE, // Error in closing a file
TEST_FS_ERROR_FREED, // Error in reading from a file
TEST_FS_ERROR_FWRITE, // Error in writing to a file
TEST_FS_ERROR_FSYNC, // Error in syncing file data
TEST_FS_ERROR_FSEEK, // Error in seeking file position
TEST_FS_ERROR_FTELL, // Error in getting file position
TEST_FS_ERROR_FREWIND, // Error in rewinding file
TEST_FS_ERROR_FSIZE, // Error in getting file size
TEST_FS_ERROR_FTRUNCATE, // Error in truncating file size
TEST_FS_ERROR_DOPEN, // Error in opening directory
TEST_FS_ERROR_DCLOSE, // Error in closing directory
TEST_FS_ERROR_DREAD, // Error in reading directory contents
TEST_FS_ERROR_DREWIND, // Error in rewinding directory
TEST_FS_ERROR_DSIZE // Error in getting directory size
} test_fs_error_t;
uint8_t f_buffer[BUFFER_SIZE]; // Buffer for file operations
// Buffer for file seek test (write)
uint8_t test_wr_buffer[SEEK_TEST_LENGTH] = {'_', '_', '_', '_', '\0'};
// Buffer for file seek test (read)
uint8_t test_rd_buffer[SEEK_TEST_LENGTH] = {'\0', '\0', '\0', '\0', '\0'};
sdspi_config_t sd_conf; // Configuration for SD card SPI interface
sdspi_physical_drive_t sdc; // Physical drive structure for SD card
fatfs_logical_drive_t fatfs_drive; // Logical drive structure for the file system
FIL fatfs_file; // FAT file structure
file_t file_obj; // File handle for file operations
test_fs_error_t error = TEST_FS_NO_ERROR; // Variable to hold file system error codes
uint8_t fs_error; // Variable for storing the result of file system functions
log_t logger; // Logger object for outputting messages
log_cfg_t config; // Logger configuration
// ------------------------------------------------------------------ FUNCTIONS
/**
* @brief Function to indicate failure by toggling an LED on the board.
*/
void test_fail() {
digital_out_init(&status, TEST_FAILED_PIN);
while(1) {
digital_out_toggle(&status);
Delay_ms(1000); // Toggle the failure LED every 1 second
}
}
// ----------------------------------------------------------------- USER CODE
/**
* @brief Main function demonstrating the file system operations on an SD card.
* The function performs the following operations:
* - Initialize the physical drive (SD card)
* - Mount the FAT file system
* - Format the drive
* - Create and manipulate a file (__DATA.TXT)
* - Test file seek functionality
*/
int main( void ) {
/* Do not remove this line or clock might not be set correctly. */
#ifdef PREINIT_SUPPORTED
preinit();
#endif
uint32_t i;
// Initialize logging over USB UART
LOG_MAP_USB_UART( config );
log_init(&logger, &config);
log_printf( &logger, "TEST STARTED\n");
// Fill buffer with test data for file write operations
for (i = 0; i < BUFFER_SIZE; i++) {
f_buffer[i] = i & 0xFF;
}
// Configure SPI pins for SD card on mikroBUS™ socket 1
sd_conf.spi_sck = MIKROBUS_1_SCK;
sd_conf.spi_miso = MIKROBUS_1_MISO;
sd_conf.spi_mosi = MIKROBUS_1_MOSI;
sd_conf.spi_cs = MIKROBUS_1_CS;
// Initialize SD card (physical drive)
fs_error = sdspi_physical_drive_init(&sdc, &sd_conf);
if (0 != fs_error) {
log_printf( &logger, "FAIL LINE: %d\n", (uint16_t)__LINE__);
test_fail();
}
// Initialize the FAT file system on the SD card
fs_error = fatfs_initialize(&fatfs_drive);
if (0 != fs_error) {
log_printf( &logger, "FAIL LINE: %d\n", (uint16_t)__LINE__);
test_fail();
}
// Mount the file system to drive number 3
fs_error = file_system_mount(&fatfs_drive.base, "3:", &sdc.base);
if (0 != fs_error) {
error = TEST_FS_ERROR_MOUNT;
log_printf( &logger, "FAIL LINE: %d\n", (uint16_t)__LINE__);
test_fail();
}
log_printf( &logger, "FORMATTING STARTED\n");
// Format the SD card (logical drive 3)
fs_error = file_system_format("3:");
if (0 != fs_error) {
error = TEST_FS_ERROR_FORMAT;
log_printf( &logger, "FAIL LINE: %d\n", (uint16_t)__LINE__);
test_fail();
}
log_printf( &logger, "FORMATTING COMPLETED\n");
// Create and open a file on drive 3 (__DATA.TXT)
fs_error = file_open(&file_obj, &fatfs_file, "3:/__DATA.TXT",
FS_FILE_OPEN_APPEND |
FS_FILE_WRITE |
FS_FILE_READ);
if (0 != fs_error) {
error = TEST_FS_ERROR_FOPEN;
log_printf( &logger, "FAIL LINE: %d\n", (uint16_t)__LINE__);
test_fail();
}
// Write buffer to the file
fs_error = file_write(&file_obj, f_buffer, BUFFER_SIZE);
if (fs_error != 0) {
error = TEST_FS_ERROR_FWRITE;
log_printf( &logger, "FAIL LINE: %d\n", (uint16_t)__LINE__);
test_fail();
}
// Write another buffer to the file (append)
fs_error = file_write(&file_obj, f_buffer, BUFFER_SIZE);
if (fs_error != 0) {
error = TEST_FS_ERROR_FWRITE;
log_printf( &logger, "FAIL LINE: %d\n", (uint16_t)__LINE__);
test_fail();
}
// Close the file
fs_error = file_close(&file_obj);
if (fs_error != 0) {
error = TEST_FS_ERROR_FCLOSE;
log_printf( &logger, "FAIL LINE: %d\n", (uint16_t)__LINE__);
test_fail();
}
log_printf( &logger, "FILE __DATA.TXT CREATED\n");
// Open the file again for file seek test
fs_error = file_open(&file_obj, &fatfs_file, "3:/__DATA.TXT",
FS_FILE_OPEN_APPEND |
FS_FILE_WRITE |
FS_FILE_READ);
if (fs_error != 0) {
error = TEST_FS_ERROR_FOPEN;
log_printf( &logger, "FAIL LINE: %d\n", (uint16_t)__LINE__);
test_fail();
}
// Seek to 16 characters before the end of the file
fs_error = file_seek(&file_obj, -16, SEEK_END);
if (fs_error != 0) {
error = TEST_FS_ERROR_FSEEK;
log_printf( &logger, "FAIL LINE: %d\n", (uint16_t)__LINE__);
test_fail();
}
// Write test data at the new cursor position
fs_error = file_write(&file_obj, test_wr_buffer, SEEK_TEST_LENGTH - 1);
if (fs_error != 0) {
error = TEST_FS_ERROR_FWRITE;
log_printf( &logger, "FAIL LINE: %d\n", (uint16_t)__LINE__);
test_fail();
}
// Seek back to the same position and read the data
fs_error = file_seek(&file_obj, -16, SEEK_END);
if (fs_error != 0) {
error = TEST_FS_ERROR_FSEEK;
log_printf( &logger, "FAIL LINE: %d\n", (uint16_t)__LINE__);
test_fail();
}
// Read the previously written data and verify
fs_error = file_read(&file_obj, test_rd_buffer, SEEK_TEST_LENGTH - 1);
if (fs_error != 0) {
error = TEST_FS_ERROR_FREED;
log_printf( &logger, "FAIL LINE: %d\n", (uint16_t)__LINE__);
test_fail();
}
// Close the file after test
fs_error = file_close(&file_obj);
if (fs_error != 0) {
error = TEST_FS_ERROR_FCLOSE;
log_printf( &logger, "FAIL LINE: %d\n", (uint16_t)__LINE__);
test_fail();
}
// Compare the read data with written data to verify the seek operation
if (memcmp(test_wr_buffer, test_rd_buffer, SEEK_TEST_LENGTH - 1) != 0) {
error = TEST_FS_ERROR_FSEEK;
log_printf( &logger, "FAIL LINE: %d\n", (uint16_t)__LINE__);
test_fail();
}
log_printf( &logger, "TEST FSEEK END COMPLETED\n");
log_printf( &logger, "TEST COMPLETED\n");
// Indicate successful test completion with LED
while (1) {
digital_out_init(&status, TEST_SUCCESS_PIN);
while (1) {
digital_out_toggle(&status);
Delay_ms(1000); // Toggle the success LED every 1 second
}
}
return TEST_FS_NO_ERROR; // Return with no error code
}
// ----------------------------------------------------------------------- END