cancel
Showing results for 
Search instead for 
Did you mean: 

USBX MSC + LVLX, USBD_STORAGE_Write and USBD_STORAGE_Read

RemiNao
Associate III

Hello, 

I am trying to set up an external flash memory (MX25V1635F) on a Nucleo-U575ZI-Q using SPI.

- I managed to simulate a disk using RAM memory, Windows formats it perfectly, which leads me to believe that my USBX configuration is correct.

- I also tested my LevelX functions for reading and writing to my NOR flash, and they seem to be working correctly, which leads me to believe that the LevelX configuration is correct.

- I disabled all my FileX functions (format, open, read, and write) because it seems that Windows (USB Host) takes over the formatting. (Btw, if FileX can't be used, how can I write a formatted file to my flash memory?).

 

My problem: when I leave the USBD_STORAGE_Write and USBD_STORAGE_Read functions empty, Windows can see the size of my disk and offers to format it (without success):

RemiNao_0-1760366801083.png

And when I implement these two functions (based on the only example I found online using the Levelx functions lx_nor_flash_sector_write and lx_nor_flash_sector_read: LINK), Windows can no longer read my disk volume or offer to format it. I think my functions are not defined correctly : 

/**
  * @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 */
  UX_PARAMETER_NOT_USED(storage_instance);
  UX_PARAMETER_NOT_USED(media_status);
  while(number_blocks--)
      {
 //         status =  fx_media_read(&nor_flash_disk,lba,data_pointer);
          status = lx_nor_flash_sector_read(&nor_flash,lba,data_pointer);
          data_pointer+=SECTOR_SIZE_LOGICAL;//512
          lba++;
          if (status != FX_SUCCESS) {
        	  Error_Handler();
          }
      }
//
//  for (UINT block = 0; block < number_blocks; block++)
//      {
//          //status =  fx_media_read(&nor_flash_disk,lba,data_pointer);
//          status = lx_nor_flash_sector_read(&nor_flash, lba + block, data_pointer);
//          data_pointer += SECTOR_SIZE_LOGICAL;//512
//          if(status != UX_SUCCESS)
//          {
//              break;
//          }
//      }

  /* 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 */
  UX_PARAMETER_NOT_USED(storage_instance);
  UX_PARAMETER_NOT_USED(media_status);
  while(number_blocks--)
     {
//         status =  fx_media_write(&nor_flash_disk,lba,data_pointer);
         status = lx_nor_flash_sector_write(&nor_flash,lba,data_pointer);
         data_pointer+=SECTOR_SIZE_LOGICAL;//512
         lba++;
         if(status != FX_SUCCESS)
         {
        	 Error_Handler();
         }
     }
//  for (UINT block = 0; block < number_blocks; block++)
//     {
//         //status = fx_media_write(&nor_flash_disk, lba + block, data_pointer);
//	       status = lx_nor_flash_sector_write(&nor_flash, lba + block, data_pointer);
//         data_pointer += SECTOR_SIZE_LOGICAL;//512
//         if(status != UX_SUCCESS)
//         {
//             break;
//         }
//     }
  /* USER CODE END USBD_STORAGE_Write */

  return status;
}

 

Also, should I use lx_nor_flash_open, and if so, where should I call it?

lx_nor_flash_open(&nor_flash, "NOR_FLASH_DISK", lx_stm32_nor_custom_driver_initialize);


Thank you for your help,

 

7 REPLIES 7
RemiNao
Associate III

I still haven't managed to resolve this issue.
For your information, here is the information from my storage instance when I do:

lx_nor_flash_open(&nor_flash, "NOR_FLASH_DISK", lx_stm32_nor_custom_driver_initialize);

RemiNao_0-1760540469454.png

Does LX_NOR_SECTOR_SIZE correspond to the size of a physical sector?
Mine are 4096 bytes (or 1024 words). And will this value define the size of the logical sectors?
I have the impression that the logical sectors need to be 512 bytes.

RemiNao_1-1760540539835.png

 

Can you help me?
thanks

 

DmitryR
Senior

Hello RemiNao,

 

block size does not need to be 512 but you should return a corresponding number in USBD_STORAGE_GetMediaBlocklength(). But it makes just little sense to use large block size for NOR so I would consider setting it to 512. Also, consider returning UX_STATE_NEXT instead of UX_SUCCESS in these two functions. You may call lx_nor_flash_open() in USBD_STORAGE_Activate().

 

Regards,

Dmitry

Hello @DmitryR 

Thank you for your help.
I forgot to mention that I’m already returning UX_STATE_NEXT and calling lx_nor_flash_open() inside USBD_STORAGE_Activate().

If you notice anything else, please let me know.

Hello RemiNao,

 

I have checked that your flash has 4K sector so you should leave this too. The one more thing I may suppose is that you use LevelX functions but you check for FileX return codes (FX_SUCCESS). That may lead to a situation when your code always ends up in the error handler.

 

With best regards,

Dmitry

Thank you,

I changed FX_SUCCESS by LX_SUCCESS, but it was not my problem.
After debbug, my program go in hardfault handler after trying de read (USBD_STORAGE_Read) only when number_blocks are superior to 1.

Here is my updated code : 

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    ux_device_msc.c
  * @author  MCD Application Team
  * @brief   USBX Device applicative file
  ******************************************************************************
   * @attention
  *
  * Copyright (c) 2025 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 */

/* USER CODE END Includes */

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

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define STORAGE_LUN_NBR                  1
#define STORAGE_BLK_NBR                  500
#define STORAGE_BLK_SIZ                  0x200
/* USER CODE END PD */

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

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
extern FX_MEDIA nor_flash_disk;
extern LX_NOR_FLASH nor_flash;


__attribute__ ((section (".usb_data"))) volatile uint8_t etx_buffer[STORAGE_BLK_NBR*STORAGE_BLK_SIZ];

/* USER CODE END PV */

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

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* 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);
  UINT status;
  status = _lx_nor_flash_open(&nor_flash, "NOR_FLASH_DISK", lx_stm32_nor_custom_driver_initialize);
  /* 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 */
  UX_PARAMETER_NOT_USED(storage_instance);
  UX_PARAMETER_NOT_USED(media_status);

  while(number_blocks>0)
      {
	  	  number_blocks--;
          status = lx_nor_flash_sector_read(&nor_flash,lba,data_pointer);
          data_pointer+=512;//512, 128
          lba++;
          if (status != LX_SUCCESS) {
        	  Error_Handler();
          }
      }

  status = UX_STATE_NEXT;
  //status = UX_SUCCESS;

  /* 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 */
  UX_PARAMETER_NOT_USED(storage_instance);
  UX_PARAMETER_NOT_USED(media_status);

  while(number_blocks>0)
     {
	  	 number_blocks--;
         status = lx_nor_flash_sector_write(&nor_flash,lba,data_pointer);
         data_pointer+=512;//512, 128
         lba++;
         if(status != LX_SUCCESS)
         {
        	 Error_Handler();
         }
     }

  status = UX_STATE_NEXT;
  //status = UX_SUCCESS;

  /* 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);

  //fx_media_flush(&nor_flash_disk);
  /* 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 */
  UX_PARAMETER_NOT_USED(storage_instance);
  UX_PARAMETER_NOT_USED(lun);
  UX_PARAMETER_NOT_USED(media_id);

  //*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 =  4096 - 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 = 512;//LX_NOR_SECTOR_SIZE; //SECTOR_SIZE_LOGICAL;

  /* USER CODE END USBD_STORAGE_GetMediaBlocklength */

  return MediaBlockLen;
}

/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

 

And here are my physical read/write functions, lx_nor_driver_read and lx_nor_driver_write.
These are called by the sector read/write functions :

static UINT lx_nor_driver_read(ULONG *flash_address, ULONG *destination, ULONG words)
{
    UINT ret = LX_SUCCESS;

    /* USER CODE BEGIN NOR_READ */

    uint32 byte_addr = (uint32)(flash_address); // + offset si besoin
    uint32 byte_len = words * sizeof(ULONG);

    if (CMD_READ(byte_addr, (uint8*)destination, byte_len) != FlashOperationSuccess){
    	ret = LX_ERROR;
    }

    /* USER CODE END  NOR_READ */

    return ret;
}

static UINT lx_nor_driver_write(ULONG *flash_address, ULONG *source, ULONG words)
{
    UINT ret = LX_SUCCESS;

    /* USER CODE BEGIN NOR_DRIVER_WRITE */

    uint32 byte_addr = (uint32)(flash_address);
    uint32 byte_len = words * sizeof(ULONG);

    // Programmer page par page (Max 256 bytes)
    uint32 offset = 0;
    uint32 chunk;
    while (offset < byte_len)
    {
    	if ((byte_len - offset) > NOR_PAGE_SIZE)
    	{
    	    chunk = NOR_PAGE_SIZE;
    	}
    	else
    	{
    	    chunk = byte_len - offset;
    	}
        if (CMD_PP(byte_addr + offset, ((uint8*)source) + offset, chunk) != FlashOperationSuccess)
        {
            ret = LX_ERROR;
            break;
        }
        offset += chunk;
    }

    /* USER CODE END  NOR_DRIVER_WRITE */

    return ret;
}



 

Hello RemiNao,

 

first of all I would want to point that one never should use hardcoded values for parameters as it is a low style of programming that leads to many problems. In this case sector size, total number of blocks and so on should be used as defines, preferably that CubeMX has done for you. This way your code will be configurable and portable. Do not tell yourself that "it is for debug only" and then you will correct all this back again. No, low programming practices do not help to debug.  

 

Then, as I have already told your flash has physical sector size of 4K bytes so it is the absolute minimum that you should use for logical sector size. Otherwise LevelX will try to erase on 512 byte blocks which for your flash is simply impossible.

 

At last, the hard fault means that the caller has reserved less memory than you use. In this particular case I think that the caller (USBX) assumes your sector size less than LevelX assumes. In this case you could surely become a hard fault even with one block, but it is a question of probability. 

 

And one more advice. Switch on stack overflow check while debugging. USBX sometimes needs surprisingly huge stack.

 

With best regards,

Dmitry

Haithem Rahmani
ST Employee

Hi @RemiNao 

I've shared a draft application under this thread.
It combines USBX Device MSC class and NOR Flash memory.

Have a look at it.

regards

Haithem.