cancel
Showing results for 
Search instead for 
Did you mean: 

"Please insert a disk into USB Drive" USBX MSC+FILEX+LVLX+THREADX

Silexman
Associate III

I am working on a project to connect QSPI flash memory to a PC as a USB mass storage device (MSC) using USBX, FILEX, LVLX, and THREADX on an STM32. This setup should allow the QSPI flash to appear as a drive in Windows.

Previously, I successfully implemented a similar project using the MCU's internal RAM as the storage medium. However, after switching to QSPI flash, I encounter the error message: 'Please insert a disk into USB Drive' when connecting the device to a PC.

To debug this issue, I tested the QSPI flash read/write functionality using FILEX + LVLX + THREADX without USBX, and it worked perfectly. This suggests the problem lies in the integration with USB MSC.

I am including my code for review. Could anyone help identify any potential errors or missing steps in my implementation? Additionally, are there specific areas I should focus on to resolve this issue? Thank you.

EDIT :

lastlba ux_device_msc.c

12 REPLIES 12

I am experiencing a similar issue. I have implemented a simple Mass Storage Class (MSC) device using the USBX stack, with the storage medium being a basic internal RAM disk.

The behavior I'm observing is unusual:

  1. Initially, the device is correctly enumerated by the PC and appears in the Device Manager list as expected.JaserTTY_2-1750220528075.png
  2. Despite this, the corresponding logical drive (F:) fails to mount in the file system, even after a significant delay of approximately one minute. 
  3. Ultimately, any attempt to access the drive F results in the error message: 'Please insert a disk into Drive F:'."

Have I missed any important considerations?

My ux msc source file:

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    ux_device_msc.c
  * @author  MCD Application Team
  * @brief   USBX Device MSC applicative source file
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2020-2021 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "ux_device_msc.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "app_usbx_device.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* Make sure this is defined, for example, in app_usbx_device.c */

// The actual memory buffer for your disk
#define STORAGE_BLK_SIZ  512 // Size of each block in bytes
#define STORAGE_BLK_NBR   40// Number of blocks in the storage
#if defined ( __ICCARM__ )
#pragma data_alignment=4
#endif
__ALIGN_BEGIN static UCHAR buffer[STORAGE_BLK_SIZ * STORAGE_BLK_NBR] __ALIGN_END;

/* USER CODE END 0 */

/**
  * @brief  USBD_STORAGE_Activate
  *         This function is called when insertion of a storage device.
  * @PAram  storage_instance: Pointer to the storage class instance.
  * @retval none
  */
VOID USBD_STORAGE_Activate(VOID *storage_instance)
{
  /* USER CODE BEGIN USBD_STORAGE_Activate */
   UX_PARAMETER_NOT_USED(storage_instance);
  /* USER CODE END USBD_STORAGE_Activate */

  return;
}

/**
  * @brief  USBD_STORAGE_Deactivate
  *         This function is called when extraction of a storage device.
  * @PAram  storage_instance: Pointer to the storage class instance.
  * @retval none
  */
VOID USBD_STORAGE_Deactivate(VOID *storage_instance)
{
  /* USER CODE BEGIN USBD_STORAGE_Deactivate  */
  UX_PARAMETER_NOT_USED(storage_instance);
  /* USER CODE END USBD_STORAGE_Deactivate */

  return;
}

/**
  * @brief  USBD_STORAGE_Read
  *         This function is invoked to read from media.
  * @PAram  storage_instance : Pointer to the storage class instance.
  * @PAram  lun: Logical unit number is the command is directed to.
  * @PAram  data_pointer: Address of the buffer to be used for reading or writing.
  * @PAram  number_blocks: number of sectors to read/write.
  * @PAram  lba: Logical block address is the sector address to read.
  * @PAram  media_status: should be filled out exactly like the media status
  *                       callback return value.
  * @retval status
  */
UINT USBD_STORAGE_Read(VOID *storage_instance, ULONG lun, UCHAR *data_pointer,
                       ULONG number_blocks, ULONG lba, ULONG *media_status)
{
  UINT status = UX_SUCCESS;

  /* USER CODE BEGIN USBD_STORAGE_Read */
  printf("USBD_STORAGE_Read: lba:%x number_blocks:%x\n",lba,number_blocks);
  memcpy(data_pointer, &buffer[lba*STORAGE_BLK_SIZ], number_blocks*STORAGE_BLK_SIZ);


  /* USER CODE END USBD_STORAGE_Read */

  return status;
}

/**
  * @brief  USBD_STORAGE_Write
  *         This function is invoked to write in media.
  * @PAram  storage_instance : Pointer to the storage class instance.
  * @PAram  lun: Logical unit number is the command is directed to.
  * @PAram  data_pointer: Address of the buffer to be used for reading or writing.
  * @PAram  number_blocks: number of sectors to read/write.
  * @PAram  lba: Logical block address is the sector address to read.
  * @PAram  media_status: should be filled out exactly like the media status
  *                       callback return value.
  * @retval status
  */
UINT USBD_STORAGE_Write(VOID *storage_instance, ULONG lun, UCHAR *data_pointer,
                        ULONG number_blocks, ULONG lba, ULONG *media_status)
{
  UINT status = UX_SUCCESS;

  /* USER CODE BEGIN USBD_STORAGE_Write */
  printf("USBD_STORAGE_Write: lba:%x number_blocks:%x\n",lba,number_blocks);
   memcpy(&buffer[lba*STORAGE_BLK_SIZ], data_pointer, number_blocks*STORAGE_BLK_SIZ);
  /* USER CODE END USBD_STORAGE_Write */

  return status;
}

/**
  * @brief  USBD_STORAGE_Flush
  *         This function is invoked to flush media.
  * @PAram  storage_instance : Pointer to the storage class instance.
  * @PAram  lun: Logical unit number is the command is directed to.
  * @PAram  number_blocks: number of sectors to read/write.
  * @PAram  lba: Logical block address is the sector address to read.
  * @PAram  media_status: should be filled out exactly like the media status
  *                       callback return value.
  * @retval status
  */
UINT USBD_STORAGE_Flush(VOID *storage_instance, ULONG lun, ULONG number_blocks,
                        ULONG lba, ULONG *media_status)
{
  UINT status = UX_SUCCESS;

  /* USER CODE BEGIN USBD_STORAGE_Flush */
  UX_PARAMETER_NOT_USED(storage_instance);
  UX_PARAMETER_NOT_USED(lun);
  UX_PARAMETER_NOT_USED(number_blocks);
  UX_PARAMETER_NOT_USED(lba);
  UX_PARAMETER_NOT_USED(media_status);
  /* USER CODE END USBD_STORAGE_Flush */

  return status;
}

/**
  * @brief  USBD_STORAGE_Status
  *         This function is invoked to obtain the status of the device.
  * @PAram  storage_instance : Pointer to the storage class instance.
  * @PAram  lun: Logical unit number is the command is directed to.
  * @PAram  media_id: is not currently used.
  * @PAram  media_status: should be filled out exactly like the media status
  *                       callback return value.
  * @retval status
  */
UINT USBD_STORAGE_Status(VOID *storage_instance, ULONG lun, ULONG media_id,
                         ULONG *media_status)
{
  UINT status = UX_SUCCESS;

  /* USER CODE BEGIN USBD_STORAGE_Status */
  *media_status = UX_SUCCESS;
  /* USER CODE END USBD_STORAGE_Status */

  return status;
}

/**
  * @brief  USBD_STORAGE_Notification
  *         This function is invoked to obtain the notification of the device.
  * @PAram  storage_instance : Pointer to the storage class instance.
  * @PAram  lun: Logical unit number is the command is directed to.
  * @PAram  media_id: is not currently used.
  * @PAram  notification_class: specifies the class of notification.
  * @PAram  media_notification: response for the notification.
  * @PAram  media_notification_length: length of the response buffer.
  * @retval status
  */
UINT USBD_STORAGE_Notification(VOID *storage_instance, ULONG lun, ULONG media_id,
                               ULONG notification_class, UCHAR **media_notification,
                               ULONG *media_notification_length)
{
  UINT status = UX_SUCCESS;

  /* USER CODE BEGIN USBD_STORAGE_Notification */
  UX_PARAMETER_NOT_USED(storage_instance);
  UX_PARAMETER_NOT_USED(lun);
  UX_PARAMETER_NOT_USED(media_id);
  UX_PARAMETER_NOT_USED(notification_class);
  UX_PARAMETER_NOT_USED(media_notification);
  UX_PARAMETER_NOT_USED(media_notification_length);
  /* USER CODE END USBD_STORAGE_Notification */

  return status;
}

/**
  * @brief  USBD_STORAGE_GetMediaLastLba
  *         Get Media last LBA.
  * @PAram  none
  * @retval last lba
  */
ULONG USBD_STORAGE_GetMediaLastLba(VOID)
{
  ULONG LastLba = 0U;

  /* USER CODE BEGIN USBD_STORAGE_GetMediaLastLba */
  LastLba = STORAGE_BLK_NBR  - 1;
  /* USER CODE END USBD_STORAGE_GetMediaLastLba */

  return LastLba;
}

/**
  * @brief  USBD_STORAGE_GetMediaBlocklength
  *         Get Media block length.
  * @PAram  none.
  * @retval block length.
  */
ULONG USBD_STORAGE_GetMediaBlocklength(VOID)
{
  ULONG MediaBlockLen = 0U;

  /* USER CODE BEGIN USBD_STORAGE_GetMediaBlocklength */
  MediaBlockLen = STORAGE_BLK_SIZ; // Standard block length for USB MSC
  /* USER CODE END USBD_STORAGE_GetMediaBlocklength */

  return MediaBlockLen;
}

/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

 

 


@simonebonetti987 wrote:

Can you confirm that you set FX_NOR_QSPI_SECTOR_SIZE to 512? 


yeah, for the msc logical sector size I use 512 byte. because other value ddin't work for me. I haven't search any further yet but I think the problem is on the nor driver.

 

 


@simonebonetti987 wrote

I also had to modify:

#define LX_STM32_OSPI_SECTOR_SIZE 512

and add also LX_STM32_OSPI_BLOCK_SIZE define so that levelX knows that the underlying flash geometry has a minimum erasable block size of 4kb.

#define LX_STM32_OSPI_BLOCK_SIZE                            4096
#define LX_STM32_OSPI_FLASH_SIZE                            W25Q16JWXHIQ_FLASH_SIZE

INT lx_stm32_ospi_get_info(UINT instance, ULONG *block_size, ULONG *total_blocks)
{
    INT status = 0;

    *block_size = LX_STM32_OSPI_BLOCK_SIZE;
    *total_blocks = (LX_STM32_OSPI_FLASH_SIZE / LX_STM32_OSPI_BLOCK_SIZE);

    return status;
}

It's quite frustrating that the concepts of "sectors" and "blocks" have different meanings through the whole levelx, usbx and stm32 nor drivers toolchain.

So basically, if i got it right, LevelX works with 512 bytes logical sectors, that are grouped in blocks. The number of sectors in one block depends on how the user configures the low level NOR driver and are set runtime by levelX when "lx_nor_flash_open" is called. In my case I used the minimum erasable unit of the flash (4kb), but also the larger 64kb block could be used.

 


yeah me too, I am new on this topic and confuse with the "sectors" and "blocks"

in my opinion, you can try the https://wiki.stmicroelectronics.cn/stm32mcu/wiki/Introduction_to_FILEX#Example_1-_SRAM_interface  to make sram levelx driver or 

Silexman_0-1750229011586.png

nor simulator interface on cube ide