STM32 USB MSC

In this tutorial we will cover how to use USB Mass Storage Class (MSC) in STM32. There will be two parts to this tutorial. In the first part we will allocate the RAM as the storage and read and write the data to this allocated portion. And in the second part, we will use the SD card as storage. SD card is connected via the SDIO interface.

Using RAM

We will use Embedded SRAM for this purpose. Check your device datasheet to know the size of the SRAM available for your device. I am using STM32F407ZE, and below is the picture from the device datasheet

According to the picture above, the 192 KB is divided between RAM and CCM. This means the actual RAM size is 192-64 = 128 KB. If you are using CUBEIDE for the project, you can see this detail in the memory region tab as shown below

You can see above that the RAM is 128 KB, and we will use some space from this RAM as the storage unit.
Let’s see the setup required in CUBE MX


CubeMX Setup

First of all, select the USB_OTG_FS and select the mode as Device_Only. Also note that the Speed is 12MBit/s


Next we need to select the USB_DEVICE and select the class as Mass Storage Class. Here you can modify the device descriptors, but i am leaving everything to default


Two pins from the microcontroller got selected automatically for this purpose, as shown in the picture below


Some Insight into the code

We will make all the changes in the usbd_storage_if.c file. It is located at CORE -> USB_DEVICE -> App. Just open the file and modify as mentioned below

#define STORAGE_LUN_NBR                  1
#define STORAGE_BLK_NBR                  200  // enter twice the size of the RAM that you want to use
#define STORAGE_BLK_SIZ                  0x200

I have 123 KB of free RAM available, as you saw in the setup part. Out of this, I am going to use 100 KB of this RAM as the storage. That’s why I have entered 200 in the STORAGE_BLK_NBR. Rest is the default configuration. Block Size is 0x200, i.e. 512 Bytes.


uint8_t buffer[STORAGE_BLK_NBR*STORAGE_BLK_SIZ];

Next, we need to define a buffer, which can hold the data. This buffer is of 100 KB in size, and the memory is used from the RAM obviously.


int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{
  /* USER CODE BEGIN 6 */
  
  memcpy(buf, &buffer[blk_addr*STORAGE_BLK_SIZ], blk_len*STORAGE_BLK_SIZ);
  
  /* USER CODE END 6 */
}

In the STORAGE_Read_Fs we need to enter our function, which will copy the data from the buffer and save it in the function’s buf


int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{
  /* USER CODE BEGIN 7 */

  memcpy(&buffer[blk_addr*STORAGE_BLK_SIZ], buf, blk_len*STORAGE_BLK_SIZ);

  /* USER CODE END 7 */
}

Similarly in the Write_Fs, the data will be copied from the function’s buf to our buffer.


Result

As you can see above, when the USB is connected, the device gets detected as the mass storage device of 100 KB size. we can create, copy, delete files and folders to or from the device.

You can check the video at the end to see the full setup and working



Using SD CARD

SD card will be used with the SDIO interface. The CubeMX setup for the USB remains same as shown earlier in this tutorial, but we need to add few more things to setup the SDIO for the SD CARD

Setup have everything set to default except the SDIOCLK divide factor. How to set this is as follows


According to STM32F4 Timer Diagram, the SDIO/MMC clock is connected to APB2. In my clock Setup, the APB2 Peripheral clock is running at 84 MHz. Now all I have to do is to bring this clock to around 12 MHz. This is because the USB is also running at the same Frequency (check the USB Speed in USB_OTG_Fs).

The formula above is to set the value of Clock divide factor, and entering the value 5, will bring the SDIO_CK to 12 MHz, i.e. 84/ (5+2).


Below is the diagram of how the SDIO SD card module is connected to the board


Some insight into the code

We will make all the changes in the usbd_storage_if.c file. It is located at CORE -> USB_DEVICE -> App. Just open the file and modify as mentioned below

int8_t STORAGE_GetCapacity_FS(uint8_t lun, uint32_t *block_num, uint16_t *block_size)
{
  /* USER CODE BEGIN 3 */
  HAL_SD_CardInfoTypeDef info;
  int8_t ret = -1;

  HAL_SD_GetCardInfo(&hsd, &info);

  *block_num =  info.LogBlockNbr  - 1;
  *block_size = info.LogBlockSize;
  ret = 0;
  return ret;
  /* USER CODE END 3 */
}

The STORAGE_GetCapacity_Fs should be replaced with the code as mentioned above. This will basically get the capacity of the SD card


int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{
  /* USER CODE BEGIN 6 */  
  
   int8_t ret = -1;

  HAL_SD_ReadBlocks(&hsd, buf, blk_addr, blk_len, HAL_MAX_DELAY);

  /* Wait until SD card is ready to use for new operation */
  while (HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER){}
  ret = 0;
  return ret;
  /* USER CODE END 6 */
}

/**
  * @brief  .
  * @param  lun: .
  * @retval USBD_OK if all operations are OK else USBD_FAIL
  */
int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{
  /* USER CODE BEGIN 7 */  
  int8_t ret = -1;

   HAL_SD_WriteBlocks(&hsd, buf, blk_addr, blk_len, HAL_MAX_DELAY);


  /* Wait until SD card is ready to use for new operation */
  while (HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER){}
  ret = 0;
  return ret;
  /* USER CODE END 7 */
}

The Storage Read and Storage Write is also replaced wit the above code.

This is it for the modifications. We don’t need to do anything with the main.c file. Just upload the code and run it


Result

When the USB is connected, the card gets detected, which is of 1 GB in size. We can perform all types of operations here.
The speed for write and read is very slow at the moment.

Check out the Video Below




Info

You can help with the development by DONATING Below.
To download the project, click the DOWNLOAD button.

Subscribe
Notify of

21 Comments
Newest
Oldest Most Voted
Inline Feedbacks
View all comments
keyboard_arrow_up