cancel
Showing results for 
Search instead for 
Did you mean: 

How to implement USB mass storage device standalone on STM32

FBL
ST Employee

Introduction

This article provides a detailed, step-by-step guide to implementing a USB mass storage class (MSC) device using the USBX middleware in standalone mode (without an RTOS) on the STM32U5 microcontroller. The example is developed and tested on the STM32U5G9J-DK1 development board featuring embedded eMMC memory.

While this guide specifically targets the STM32U5 board, the principles and code structure can be easily adapted for other STM32 microcontrollers using USBX and USB MSC functionality. This includes but is not limited to the STM32H5 series with minor modifications. This flexibility makes the solution valuable for a wide range of USB MSC standalone applications.

Prerequisites

1. Project setup

You can directly clone the project from STM32-hotspot/CKB-STM32-MSC-Standalone-U5 or follow the steps below to set your project.

1.1 Create a project

  • Start by creating a new project using the STM32CubeIDE by clicking [File -> New -> STM32 Project]. Use the [Board Selector] tab and select the [STM32U5G9J-DK1]. 

FBL_0-1758562183659.png

  • Initialize all peripherals with their default mode.

FBL_1-1758562183661.png

 

  • Disable TrustZone®

FBL_2-1758562183664.png

 

  • In the System View, deconfigure any unnecessary peripherals to optimize resources.

FBL_0-1758566670769.png

  • In the clock configuration tree viewer, set HCLK to maximum speed.

FBL_0-1758568646447.png

  • Configure SDMMC1 clock source as CLK48 and USBHS clock source as HSE

FBL_1-1758568754131.png

1.2 Configure USBX middleware

  • Enable the USBX Device middleware package.

FBL_9-1758562183684.png

  • Configure the following parameters:

FBL_10-1758562183689.png

  • Configure the UXDevice memory pool size and USBX Device System Stack Size appropriately:

 

FBL_11-1758562183692.pngFBL_12-1758562183695.png

2.3 Code generation

  • In Code Generator Settings, set all free GPIO pins to Analog mode to reduce power consumption.

FBL_13-1758562183698.png

  • After completing the USBX and peripheral configurations in STM32CubeMX, save your project and generate the initialization code.

2. Code editing

Once the code is generated, perform the following steps to integrate the eMMC interface and ensure proper USB operation:

  • Add BSP Driver for eMMC
    Include the BSP driver source file stm32u5x9j_discovery_mmc.c and its headers into your project. This file provides the necessary board support package (BSP) functions to interface with the eMMC memory.

  • Enable required HAL modules
    In the stm32u5xx_hal_conf.h file, enable the following HAL modules if they are not already enabled, as they may be required by the BSP.

  • Exclude SDMMC generated code
    Since the eMMC interface is managed by the BSP driver, exclude or disable the SDMMC peripheral code automatically generated by CubeMX to avoid conflicts or redundant initialization.

2.1 main.c

  1. Declare between /* USER CODE BEGIN PV */ and /* USER CODE END PV */
/* USER CODE BEGIN PV */
#define BLOCK_START_ADDR         0     /* Block start address      */
#define NUM_OF_BLOCKS            5     /* Total number of blocks   */
#define BUFFER_WORDS_SIZE        ((MMC_BLOCKSIZE * NUM_OF_BLOCKS) >> 2) /* Total data size in bytes */
int32_t MMC_state=0 ;
/* USER CODE END PV */
  1. In main.c, add between   /* USER CODE BEGIN SysInit */ and   /* USER CODE END SysInit */
/* USER CODE BEGIN SysInit */  
MMC_state = BSP_MMC_Init();
  if(MMC_state != BSP_ERROR_NONE)
  {
    printf("\r\nMMC Initialization : Failed");
    printf("\r\n");
  }
/* USER CODE END SysInit */
  1. Add between     /* USER CODE BEGIN 3 */ and     /* USER CODE END 3 */
/* USER CODE BEGIN 3 */
    USBX_Device_Process(NULL);
}
/* USER CODE END 3 */

2.2 usb_otg.c

Make sure to add in usb_otg.c specifically in HAL_PCD_MspInit() between /* USER CODE BEGIN USB_OTG_HS_MspInit 0 */ and /* USER CODE END USB_OTG_HS_MspInit 0 */ if CubeMX does not generate it correctly 

 /* USER CODE BEGIN USB_OTG_HS_MspInit 0 */
    __HAL_RCC_SYSCFG_CLK_ENABLE();
  /* USER CODE END USB_OTG_HS_MspInit 0 */

2.3 app_usbx_device.c

  • In app_usbx_device.c, add between /* USER CODE BEGIN PV */ and /* USER CODE END PV */
 /* USER CODE BEGIN PV */
__ALIGN_BEGIN USB_MODE_STATE USB_Device_State_Msg __ALIGN_END;
extern PCD_HandleTypeDef            hpcd_USB_OTG_HS;
 /* USER CODE END PV */
  • In the same file add a new function to initialize USBX device between /* USER CODE BEGIN 1 */ and /* USER CODE END 1 */
 /**
  * @brief  MX_USBX_Device_Process
  *         Run USBX state machine.
  *   arg: not used
  * @retval none
  */
VOID USBX_Device_Process(VOID *arg)
{
  ux_device_stack_tasks_run();
}
VOID USBX_APP_Device_Init(VOID)
{
  /* USER CODE BEGIN USB_Device_Init_PreTreatment_0 */
  /* USER CODE END USB_Device_Init_PreTreatment_0 */
  /* initialize the device controller HAL driver */
  MX_USB_OTG_HS_PCD_Init();
  /* USER CODE BEGIN USB_Device_Init_PreTreatment_1 */
  HAL_PCDEx_SetRxFiFo(&hpcd_USB_OTG_HS, 0x200);
  HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS, 0, USBD_MAX_EP0_SIZE / 4);
  HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS, 1, USBD_MSC_EPIN_HS_MPS / 2);
  HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS, 2, USBD_MSC_EPOUT_HS_MPS / 2);
  /* USER CODE END USB_Device_Init_PreTreatment_1 */
  /* initialize and link controller HAL driver to USBx */
     if(_ux_dcd_stm32_initialize((ULONG)USB_OTG_HS, (ULONG)&hpcd_USB_OTG_HS) != UX_SUCCESS)
  {
                Error_Handler();
  }
  /* USER CODE BEGIN USB_Device_Init_PostTreatment */
  HAL_PCD_Start(&hpcd_USB_OTG_HS);
  /* USER CODE END USB_Device_Init_PostTreatment */
}
  • After compiling the project, a warning may appear:
Warning[Pe223]: function "MX_USB_OTG_HS_PCD_Init" declared implicitly     C:\xx\CKB-STM32-MSC-Standalone-U5\USBX\App\app_usbx_device.c   357

To avoid this warning, you can simply include #include "usb_otg.h" in the header file.

2.4 app_usbx_device.h

  • Call the function USBX_APP_Device_Init() between   /* USER CODE BEGIN MX_USBX_Device_Init1 */ and   /* USER CODE END  MX_USBX_Device_Init1 */
 /* USER CODE BEGIN  MX_USBX_Device_Init1 */
  USBX_APP_Device_Init();
 /* USER CODE END  MX_USBX_Device_Init1 */
  • Define the prototypes in the header file between /* USER CODE BEGIN EFP */ and /* USER CODE END EFP */
/* USER CODE BEGIN EFP */
VOID USBX_APP_Device_Init(VOID);
VOID USBX_Device_Process(VOID *arg);
/* USER CODE END EFP */
  • In the same header file, add USB_MODE_STATE between /* USER CODE BEGIN 1 */ and /* USER CODE END 1 */
 /* USER CODE BEGIN 1 */
typedef enum
{
  STOP_USB_DEVICE = 1,
  START_USB_DEVICE,
} USB_MODE_STATE;
 /* USER CODE END 1 */

2.5 ux_device_msc.c

Now, implement the following USB MSC storage callbacks with eMMC support:

2.5.1 Get media last LBA

Add between  /* USER CODE BEGIN USBD_STORAGE_GetMediaLastLba */ and  /* USER CODE END USBD_STORAGE_GetMediaLastLba */

  /* USER CODE BEGIN USBD_STORAGE_GetMediaLastLba */
if(eMMC_Init == 0)
{
  eMMC_Init = 1;
  BSP_MMC_Init();
}
  HAL_MMC_CardInfoTypeDef pCardInfo = {0};
  HAL_MMC_GetCardInfo(&uSdHandle, &pCardInfo);
  LastLba = pCardInfo.BlockNbr-1;
  /* USER CODE END USBD_STORAGE_GetMediaLastLba */

2.5.2 Get media block length

Add between  /* USER CODE BEGIN USBD_STORAGE_GetMediaBlocklength */ and  /* USER CODE END USBD_STORAGE_GetMediaBlocklength */ 

  /* USER CODE BEGIN USBD_STORAGE_GetMediaBlocklength */  
  if(eMMC_Init == 0)
  {
    eMMC_Init = 1;
    BSP_MMC_Init();
  }
  HAL_MMC_CardInfoTypeDef pCardInfo = {0};
  HAL_MMC_GetCardInfo(&uSdHandle, &pCardInfo);
    MediaBlockLen = pCardInfo.BlockSize;
  /* USER CODE END USBD_STORAGE_GetMediaBlocklength */

2.5.3 Read blocks

Add between  /* USER CODE BEGIN USBD_STORAGE_Read */ and  /* USER CODE END USBD_STORAGE_Read*/

/* USER CODE END USBD_STORAGE_Read */
  uint32_t timeout = 10000;
//  UX_PARAMETER_NOT_USED(storage_instance);
//  UX_PARAMETER_NOT_USED(lun);
//  UX_PARAMETER_NOT_USED(media_status);
  if (BSP_MMC_ReadBlocks((uint32_t*)data_pointer, lba, number_blocks, timeout) == BSP_ERROR_NONE)
  {
    // Wait until MMC card is ready for next operation
    while (BSP_MMC_GetCardState() != MMC_TRANSFER_OK)
    {
      if (timeout-- == 0)
      {
           while(1);
      }
    }
    status = UX_STATE_NEXT;
  } else
 {
   while(1);
 }
  /* USER CODE END USBD_STORAGE_Read */

2.5.4 Write blocks

Add between  /* USER CODE BEGIN USBD_STORAGE_Write */ and  /* USER CODE END USBD_STORAGE_Write */

 /* USER CODE BEGIN USBD_STORAGE_Write */
  uint32_t timeout = 10000;
//  UX_PARAMETER_NOT_USED(storage_instance);
//  UX_PARAMETER_NOT_USED(lun);
//  UX_PARAMETER_NOT_USED(media_status);
 if (BSP_MMC_WriteBlocks((uint32_t*)data_pointer, lba, number_blocks, timeout) == BSP_ERROR_NONE)
 {
   // Wait until MMC card is ready for next operation
   while (BSP_MMC_GetCardState() != MMC_TRANSFER_OK)
   {
      if (timeout-- == 0)
     {
      status= UX_ERROR;  // Timeout expired
      }
   }
   status = UX_STATE_NEXT;
 }
 else
 {
   while(1);
 }
  /* USER CODE END USBD_STORAGE_Write */

Do not return status = UX_SUCCESS; in USBD_STORAGE_Read, USBD_STORAGE_Write, or similar USBX MSC callbacks. Use instead: status = UX_STATE_NEXT;

3. Build and flash the application

After building and flashing the firmware to your STM32U5G9J-DK1 board:

  • Connect the board to a PC host via USB.
  • The device should enumerate as a USB mass storage device.
  • The PC detects a new removable drive representing the eMMC storage.
  • Standard file operations (read, write, format) can be performed on this drive.

4. Expected results

FBL_19-1758563672633.pngFBL_20-1758563685902.pngFBL_21-1758563704679.png

  • STM32U5G9J-DK1 enumerates successfully as a USB MSC device.
  • All necessary USB descriptors (device, configuration, string) are correctly provided.
  • The eMMC memory (4 GB volume) is accessible as a removable drive on the host system.

Conclusion

This guide demonstrates how to implement a USB mass storage class device on the STM32U5 platform using USBX middleware in standalone mode. The device interfaces with onboard eMMC memory and appears as a standard removable USB drive to the host PC.

Related links

 

Version history
Last update:
‎2025-09-26 2:49 AM
Updated by:
Contributors