cancel
Showing results for 
Search instead for 
Did you mean: 

How to use FileX with eMMC for file system management

B.Montanari
ST Employee

Summary

This article guides you on how to use FileX, a fully compliant FAT library for media storage and file system management with eMMC. In this example, the selected hardware is an STM32U5A9-DK board featuring anintegrated eMMC card readily available.

1. What is FileX?

FileX is a middleware that contains a robust and high-performance file allocation table (FAT)-compatible file system. FileX can be used in standalone mode and it is also easily integrated with Azure® RTOS ThreadX and with Azure® RTOS LevelX. Engineered with a focus on minimal footprint and optimal performance, FileX proves ideal for embedded applications necessitating efficient file management operations and with limited media storage. Its compatibility extends to various physical media, encompassing RAM, USBX, SD card, eMMC card, and NAND/NOR flash memories facilitated through LevelX.

FileX supports FAT16, FAT32, and extFAT formats. It offers a set of APIs to deal with files and directories such as create, delete, and read/write. FileX supports both utf8 and unicode coding as well as the "Long File Name” features to ensure file system compatibility between MCU and modern PCs. It also ensures the integrity of the file system via the failsafe feature (fault tolerance.) This especially is the case for flash memories where "power loss" may damage the data content and break the whole file system.

1.1 What is an eMMC card?

The eMMC, an abbreviation for "Embedded Multi-Media Card," combines flash memory and a flash memory controller on a single silicon die. Comprising the MMC interface, memory, and controller within a BGA package. Its "embedded" nature signifies direct soldering onto the device's board, ensuring stability, and minimizing displacement risks. It uses NAND flash technology, just like SD cards.

1.2 Hardware prerequisite 

For this project, we utilize the STM32U5A9J-DK discovery kit, one of the few hardware evaluation boards we have that allow validating the eMMC implementation.

2. STM32CubeMX Settings and Configurations

Let us start by creating a new STM32 project following the steps in the image.

BMontanari_0-1706718933631.png

After these steps, you will be directed to the "Target Selection" tab. Insert the part number STM32U5A9NJH6Q in the Search Box as shown in the image below.

BMontanari_1-1706718933638.png

Click on next and name your project. Click on finish to open the ioc file.

BMontanari_2-1706718933639.png

2.1 GPIO Configuration  

To start configuring the system, we begin with the GPIO peripheral. For this project we use two LEDs, green and red. The green LED will blink to indicate that the file system is functioning properly. The red LED will be set to a high level in the error handler to indicate an issue. We designate the PE0 and PE1 pins as a GPIO output push-pull mode and label them as GREEN_LED and RED_LED respectively.

2.2 SDMMC1 Configuration

Enable the peripheral by selecting Mode "MMC 8 bits Wide bus" with the parameter settings remaining as default.

BMontanari_3-1706718933642.png

In addition, we need to configure the NVIC settings to select the SDMMC1 global interrupt as the code uses a few callbacks based on the ISR. These callbacks are responsible for managing the transmission and reception flags of messages between the eMMC and the MCU.

BMontanari_4-1706718933643.png

To ensure correct eMMC pinout connections were assigned, navigate to the GPIO Settings and refer to the table below for comparison.

BMontanari_5-1706718933645.png

2.3 ThreadX Configuration

As mentioned, this example uses the RTOS, but a similar approach could be done in bare metal mode. Navigate to the "Middleware and Software Packs" section and locate ThreadX. To enable it, navigate to the Mode selection, click on the "Core" box, and keep the configuration as default.

BMontanari_6-1706718933646.png

2.4 LevelX Configuration

LevelX is a powerful library that provides wear-leveling and bad-block management features for flash memories. While it does not offer file system APIs, it does provide low-level APIs for reading, writing, and erasing sectors in flash memories. When combined with FileX, it enables seamless use of NAND and NOR flash memories as media storage devices.

BMontanari_7-1706718933648.png

To enable it, locate LEVELX as shown in the image above. Click on the "LevelX NAND Flash Support" box, all other configurations remain as their default settings.

2.5 FileX Configuration

To activate the FileX library, simply select the "FileX Core" checkbox and choose the "MMC interface" option in File System interfaces. The configurations can remain at their default settings.

BMontanari_8-1706718933650.png

All set, you can save and generate the code!

3. STM32CubeIDE Code Development

3.1  main.c

There are not many modifications to be made in the main.c file, the only function that has its code modified is the Error_Handler. This function is responsible for handling errors that may occur throughout the application. When this function is called, it indicates that an error has occurred and the RED_LED is set to a high level, causing it to illuminate.

Finally, the function disables interrupts and enters an infinite loop to prevent further execution of the code.

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
	HAL_GPIO_WritePin(LD4_GPIO_Port, LD4_Pin, SET);
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

3.2 app_filex.c

Most of the modifications are made in this file, which can be accessed by navigating to the FileX folder in the Project Explorer. Upon opening the folder, you find the App folder, which contains the desired app_filex.c file.

Firstly, we must define three constants in the USER CODE PD section.

/* USER CODE BEGIN PD */
#define MMC_NUM_OF_SECTORS            5     /* Total number of sectors   */
#define MMC_SECTORSIZE   ((uint32_t)512U) /*!< Sector size is 512 bytes */
#define MMC_SIZE        ((MMC_SECOTRSIZE * NUM_OF_SECTORS) >> 2) /* Total data size in bytes */
/* USER CODE END PD */

The first constant, MMC_NUM_OF_SECTORS, is assigned a value of 5 and represents the total number of memory sectors.
The second constant, MMC_SECTORSIZE, is assigned a value of 512 and represents the sector size in bytes.
The third constant, MMC_SIZE, is calculated by multiplying the sector size by the total number of sectors and then shifting the result right by 2 bits. This results in the total size of the MMC in bytes.

In addition, it is necessary to declare the RTOS native data structures, one to manage the files accessed by FileX and another to handle the message traffic by the ThreadX queue.

/* USER CODE BEGIN PV */
/* Define FileX global data structures.  */
FX_FILE fx_file;
/* Define ThreadX global data structures.  */
TX_QUEUE tx_msg_queue;
/* USER CODE END PV */

Do not forget to declare the Error_Handler prototype in the application scope.

/* USER CODE BEGIN PFP */
void Error_Handler(void);
/* USER CODE END PFP */

The following code lines deals with formatting the media storage device using the _fx_media_format API. It is necessary to allocate the file system inside the eMMC properly. The function for this API includes several parameters, such as:

  • The eMMC_disk pointer
  • Driver entry
  • Device info pointer
  • Media buffer pointer (and its size)
  • Volume name
  • Number of FATs
  • Directory entries
  • Hidden sectors
  • Total number of sectors (and their sizes)
  • Sectors per cluster and per track

The function returns a status code that indicates whether the operation was successful and whether it is not. Additionally, the code includes a read buffer and a data buffer that is used to test the functionality of the file system once it has been formatted.

 

/* USER CODE BEGIN fx_app_thread_entry 0*/
  UINT status;
  	ULONG bytes_read;
  	CHAR read_buffer[32];
  	CHAR data[] = "This is FileX working on STM32";

  	mmc_status = fx_media_format(&mmc_disk,					// eMMC_disk pointer
  			fx_stm32_mmc_driver,								 // Driver entry
  			(VOID*) FX_NULL,							// Device info pointer
  			(VOID*) fx_mmc_media_memory,				// Media Buffer Pointer
  			sizeof(fx_mmc_media_memory),					// Media Buffer Size
  			FX_MMC_VOLUME_NAME,                            	   // Volume Name
  			FX_MMC_NUMBER_OF_FATS,                             // Number of FATs
  			32,                                             // Directory Entries
  			FX_MMC_HIDDEN_SECTORS,                             // Hidden sectors
  			MMC_NUM_OF_SECTORS, 								// Total sectors
  			MMC_SECTORSIZE,		                               // Sector size
  			1,                                            // Sectors per cluster
  			1,                                                  // Heads
  			1);                                             // Sectors per track
/* USER CODE END fx_app_thread_entry 0*/

Upon opening the eMMC driver, a section titled "USER CODE MMC open error" is presented to handle any potential errors that may arise during this process. Within this section, a call to the Error_Handler( ) is implemented.

/* Open the disk driver */
  mmc_status =  fx_media_open(&mmc_disk, FX_MMC_VOLUME_NAME, fx_stm32_mmc_driver, (VOID *)FX_NULL, (VOID *) fx_mmc_media_memory, sizeof(fx_mmc_media_memory));
/* Check the media open mmc_status */
  if (mmc_status != FX_SUCCESS)
  {
    /* USER CODE BEGIN MMC open error */
    Error_Handler();
    /* USER CODE END MMC open error */
  }

 

A similar scenario occurs when creating a new file. If the process does not return a successful file creation status, the Error_Handler() function must be called. According to the verification logic, it is necessary to add this calling within the conditionals, as shown in the image below.

/* USER CODE BEGIN fx_app_thread_entry 1*/
  /* Create a file called STM32.TXT in the root directory.  */
      status = fx_file_create(&mmc_disk, "STM32.TXT");
      /* Check the create status.  */
      if (status != FX_SUCCESS) {
             /* Check for an already created status. This is expected on the
              second pass of this loop!  */
             if (status != FX_ALREADY_CREATED) {
                   /* Create error, call error handler.  */
                   Error_Handler();
             }
      }

Throughout the application, the Error_Handler() function must be called multiple times. These calls occur after evaluating the status variable, and if it has a value different from FX_SUCCESS, the handler must be called.

Below are the code sections where this function should be called:

 

  • After opening the selected file for writing.
/* Open the test file.  */
      status = fx_file_open(&mmc_disk, &fx_file, "STM32.TXT", FX_OPEN_FOR_WRITE);
      /* Check the file open status.  */
      if (status != FX_SUCCESS) {
             /* Error opening file, call error handler.  */
             Error_Handler();
      }
  • After searching for the beginning of the file.
/* Seek to the beginning of the test file.  */
      status = fx_file_seek(&fx_file, 0);
      /* Check the file seek status.  */
      if (status != FX_SUCCESS) {
             /* Error performing file seek, call error handler.  */
             Error_Handler();
      }
  • After writing the contents of the data buffer to the file.
/* Write a string to the test file.  */
      status = fx_file_write(&fx_file, data, sizeof(data));
      /* Check the file write status.  */
      if (status != FX_SUCCESS) {
             /* Error writing to a file, call error handler.  */
             Error_Handler();
      }
  • After closing the selected file after writing.
/* Close the test file.  */
      status = fx_file_close(&fx_file);
      /* Check the file close status.  */
      if (status != FX_SUCCESS) {
             /* Error closing the file, call error handler.  */
             Error_Handler();
      }
  • After flushing all cached data to the physical media.
status = fx_media_flush(&mmc_disk);
/* Check the media flush  status.  */
      if (status != FX_SUCCESS) {
             /* Error closing the file, call error handler.  */
             Error_Handler();
      }
  • After opening the selected file for reading.
/* Open the test file.  */
      status = fx_file_open(&mmc_disk, &fx_file, "STM32.TXT", FX_OPEN_FOR_READ);
      /* Check the file open status.  */
      if (status != FX_SUCCESS) {
             /* Error opening file, call error handler.  */
             Error_Handler();
      }
  • After reading the first 28 bytes in the selected file.
/* Read the first 28 bytes of the test file.  */
      status = fx_file_read(&fx_file, read_buffer, sizeof(data), &bytes_read);
      /* Check the file read status.  */
      if ((status != FX_SUCCESS) || (bytes_read != sizeof(data))) {
             /* Error reading file, call error handler.  */
             Error_Handler();
      }
  • After comparing if the data read from the file is equal to the written one.
/* Check if the read data is equal to the written data.  */
      if (memcmp(read_buffer, data, sizeof(data)) != 0) {
             /* Error reading file, call error handler.  */
             Error_Handler();
      } else {
             HAL_GPIO_TogglePin(LD3_GPIO_Port, LD3_Pin);
      }
  • After closing the selected file after reading.
/* Close the test file.  */
      status = fx_file_close(&fx_file);
      /* Check the file close status.  */
      if (status != FX_SUCCESS) {
             /* Error closing the file, call error handler.  */
             Error_Handler();
      }
  • After closing the eMMC driver.
/* Close the media.  */
      status = fx_media_close(&mmc_disk);
      /* Check the media close status.  */
      if (status != FX_SUCCESS) {
             /* Error closing the media, call error handler.  */
             Error_Handler();
      }
/* USER CODE END fx_app_thread_entry 1*/

 

4. Debug and Results

The debug logic for this application includes LED functionality and variable monitoring. The green LED blinks to indicate the system's good functioning, while the red LED is set to a high level and shines continuously when an error occurs.

Additionally, the STM32CubeIDE debug perspective provides a "Variables" tab that displays every declared variable. This enables easy monitoring of the read_buffer behavior and providing valuable insight into the system's performance.

At the start of the application, the read_buffer is initialized with default values in all of its positions, as shown in the image below.

BMontanari_9-1706718933651.png

 

After initializing the data variable with the string "This is FileX working on STM32" and subsequently writing its contents to the designated file, the values stored in the read_buffer are altered as a result of this operation. Please refer to the image below for verification.

BMontanari_10-1706718933654.png

Conclusion

In conclusion, the implementation of the file system using FileX and eMMC has been a successful endeavor. The use of FileX, with its robust and high-performance (FAT)-compatible file system, has allowed us to create a reliable and efficient application. It serves as an excellent example of how to implement a file system in an embedded application.

The eMMC card, with its low power consumption and large storage capacity, has proven to be an excellent media choice. The _fx_media_format API was used to format the eMMC, ensuring proper allocation of the file system inside the storage device.

The read_buffer and data variables were used to test the functionality of the file system once it was formatted. This project addresses the step by step implementation to format and use eMMC memory using FileX. This creates a readily available code that can be used or edited to address your development requirements.

Related links

ST wiki introduction to FileX: https://wiki.st.com/stm32mcu/wiki/Introduction_to_FILEX

ST wiki introduction to LevelX: https://wiki.st.com/stm32mcu/wiki/Introduction_to_LEVELX

ST wiki introduction to ThreadX: https://wiki.st.com/stm32mcu/wiki/Introduction_to_THREADX

FileX official documentation: https://learn.microsoft.com/en-us/azure/rtos/filex/

 

We also brought official demos from ST for the STM32U575I-EV board, which proved to be very useful resources in the development of this article:
https://github.com/STMicroelectronics/STM32CubeU5/tree/main/Projects/STM32U575I-EV/Applications/FileX/Fx_MultiAccess

https://github.com/STMicroelectronics/STM32CubeU5/tree/main/Projects/STM32U575I-EV/Applications/FileX/FX_IAP

https://github.com/STMicroelectronics/STM32CubeU5/tree/main/Projects/STM32U575I-EV/Applications/FileX/Fx_uSD_File_Edit

 

 

Version history
Last update:
‎2024-03-11 02:05 AM
Updated by: