STM32 and USB. Mass Storage + SD Card.

Within series of articles devoted to STM32Cube we start discussing different USB modes. And today we’ll realize USB Mass Storage Device class with SD-Card connected to the MCU. Thus, microcontroller STM32F10x acting as a cardreader will be the result of this post 🙂

STM32 Mass storage device

As I’ve mentioned at the beggining of the post, I’ll use STM32F10x microcontroller. SD-card will be connected via SDIO interface. So, let’s run STM32Cube and begin the configuring of MCU units!

First of all, we should enable high-speed external oscillator, USB and SDIO units. Moreover, the proper USB mode should be selected. In this case it is Mass Storage Class mode:

Mass Storage Class

Please note that when we enable any microcontroller unit, STM32CubeMx automatically marks all involved pins.

In addition, I’ll use PA10 as USB_DISCONNECT_PIN – it is responsible for software connection and disconnection of USB. So, I’ll configure this GPIO as “GPIO_Output”.

General configuration is quite clear, and now we proceed to the “Clock configuration” tab. All the frequencies can be set there. While working with USB, the most important point of clock configuration is 48 MHz at USB clock:

STM32Cube clock settings

After that we can do some extra configuration. In order to do this let’s open the “Configuration” tab and click on SDIO unit. As a result a new window will appear, where we would be able to set SDIO clock prescaler:

STM32Cube SDIO settings

In principle, after that all units are configured properly, so we can start the project generation process!

When the generation is finished, the project is ready for programming the MCU, but we have to make some modifications in the source files. Firstly, I’ll reset the output state of USB_DISCONNECT_PIN:

/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_SDIO_SD_Init();
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_10, GPIO_PIN_RESET);
MX_USB_DEVICE_Init();

As you see, Cube has already initialised SDIO and USB, but it doesn’t take care about relations between these units. Thus, we should edit usbd_storage_if.c file, where all the functions needed for USB interface are located:

STORAGE_Init_FS,
STORAGE_GetCapacity_FS,
STORAGE_IsReady_FS,
STORAGE_IsWriteProtected_FS,
STORAGE_Read_FS,
STORAGE_Write_FS,
STORAGE_GetMaxLun_FS,
(int8_t *)STORAGE_Inquirydata_FS

STMCube doesnt’t fill this functions, so they are empty. Let’s edit some of them in order to make our device working! Firstly, we should correct the block size:

#define BLOCK_SIZE                       512

Secondly, STORAGE_GetCapacity_FS() function has to be modified. To get SD-card full size we can use HAL_SD_Get_CardInfo(). This function takes two arguments which are defined in the main.c file. So, they should be defined in usbd_storage_if.c file too:

extern SD_HandleTypeDef hsd;
extern HAL_SD_CardInfoTypedef SDCardInfo;

STORAGE_GetCapacity_FS() should return the block size and the number of blocks, and now we can calculate it. We should just divide the total size by the size of one block:

int8_t STORAGE_GetCapacity_FS (uint8_t lun, uint32_t *block_num, uint16_t *block_size)
{
  /* USER CODE BEGIN 3 */   
  HAL_SD_Get_CardInfo(&hsd, &SDCardInfo);
  *block_num  = SDCardInfo.CardCapacity / BLOCK_SIZE;
  *block_size = BLOCK_SIZE;
  return (USBD_OK);
  /* USER CODE END 3 */ 
}

Thirdly, the read and write functions should be implemented:

/*******************************************************************************
* Function Name  : STORAGE_Read_FS
* Description    : 
* Input          : None.
* Output         : None.
* Return         : None.
*******************************************************************************/
int8_t STORAGE_Read_FS (uint8_t lun, 
                        uint8_t *buf, 
                        uint32_t blk_addr,                       
                        uint16_t blk_len)
{
  /* USER CODE BEGIN 6 */ 
  HAL_SD_ReadBlocks(&hsd, (uint32_t*)buf, (uint64_t)(blk_addr * BLOCK_SIZE), BLOCK_SIZE, blk_len);
 
  return (USBD_OK);
  /* USER CODE END 6 */ 
}
 
/*******************************************************************************
* Function Name  : STORAGE_Write_FS
* Description    :
* Input          : None.
* Output         : None.
* Return         : None.
*******************************************************************************/
int8_t STORAGE_Write_FS (uint8_t lun, 
                         uint8_t *buf, 
                         uint32_t blk_addr,
                         uint16_t blk_len)
{
  /* USER CODE BEGIN 7 */ 
  HAL_SD_WriteBlocks(&hsd, (uint32_t*)buf, (uint64_t)(blk_addr * BLOCK_SIZE), BLOCK_SIZE, blk_len);
 
  return (USBD_OK);
  /* USER CODE END 7 */ 
}

We’ve just added the code which will read/write data from/to SD-card connected to SDIO interface.

Now the project is ready for compiling and programming the MCU! If we connect STM32 to the PC, we’ll see a new device in our system. Thus, we can create, edit and delete files on SD-card.

Well, I’ll stop here, if you have any questions, you can add a comment on this post 🙂

If you have problems with USB you can try to increase Stack_size and Heap_size in startup_stm32xxxx.s/ Sometimes it helps =)

Like this post? Suggest to friends!

104 thoughts on “STM32 and USB. Mass Storage + SD Card.
  1. Hi Aveal,
    I try to do the MSC-FATfs connection on a custom board but fail at GetCapacity with getting zeros back. How does the underlying function know which SD card I wan to use? Is is correct to pass an empty/undefined hsd?

    Thanks, Kaf

    • Hi!

      Cube should fill the hsd structure with proper values during the initialization process. We just correct the BLOCK_SIZE.

      • Any idea where it does that? MX_FATFS_Init() links the driver to the SDcard but doesn’t seem to initialize anything. MX_SDIO_SD_Init() seems to initialize the low-lever stuff only.

        What version of CubeMX are you actualy using? My CubeMX-generated usbd_storage_if.c looks a bit different anyway, block number and sizes are pre-defined, probably to have a default. Here’s an excerpt:
        […]
        #define STORAGE_LUN_NBR 1
        #define STORAGE_BLK_NBR 0x10000
        #define STORAGE_BLK_SIZ 0x200
        […]
        int8_t STORAGE_GetCapacity_FS (uint8_t lun, uint32_t *block_num, uint16_t *block_size)
        {
        /* USER CODE BEGIN 3 */
        *block_size = STORAGE_BLK_SIZ;
        *block_num = STORAGE_BLK_NBR;
        return (USBD_OK);
        /* USER CODE END 3 */
        }

        • For this project I’ve used the old version of STM32Cube. When I worked with NAND memory and USB MSD I passed all parameters directly into the function STORAGE_GetCapacity_FS():

          *block_size = mySize;
          *block_num = myCount;

          • what does mySize and myCount mean ? I have same problem : can’t use fatfs with your tutorial

  2. Hi, Thank you very much for take the time to make this tutorial. I am working in a project in which i have to access to an eeprom instead of an sd. Could you bring me some help with how i have to configure the libraries?

    Thank you.

    • Hello!

      You need just to change read/write functions. For example:
      int8_t STORAGE_Read_FS (uint8_t lun,
      uint8_t *buf,
      uint32_t blk_addr,
      uint16_t blk_len)
      {
      EEPROM_Write(…..);

      return (USBD_OK);
      }

      You should also define functions to access to EEPROM (these functions are usually based in I2C bus driver).

      • Hi Aveal,

        Thanks for your response. Did you get something like this working? I am also in trouble with the inicialization of the i2c with thehal libraries. I can not understand at all how i have to modify the function:

        __weak void HAL_I2C_MspInit(I2C_HandleTypeDef *hi2c)
        {
        /* Prevent unused argument(s) compilation warning */
        UNUSED(hi2c);
        /* NOTE : This function should not be modified, when the callback is needed,
        the HAL_I2C_MspInit could be implemented in the user file
        */
        }

        Thanks again,

        • You have no need to modify it. After configuring I2C peripheral with STM32Cube you can call functions:
          HAL_I2C_Master_Transmit();
          HAL_I2C_Master_Receive();
          for read/write operations. All the initialization is done by the STM32Cube utility automatically.

          • Are you sure? I guess that the cube utility inicialization rutines dont configure the i2c pins as open drain.

  3. Hi, i could build the read/write eeprom rutines. But i dont know what parameters of:

    int8_t STORAGE_Write_FS (uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)

    i have to pass to the read/write eeprom rutines. I understand that de minimal block size is 512 bytes but my eeprom buffer is 64 bytes, so that means that i have to do something in the middle. Could you please help with this?

    • You should divide your memory into 512 bytes blocks. Each of them should have its own number. So in write function you have to write this blocks with data passed through *buf pointer.

      • ok, so it means that Buf can point between 1 and 512 bytes. Thats write?

        How is organize the blk address? If i had 16 blocks of 512 bytes, the blk addresses that i receive from the function parameters would be 0,1,2,3,…,14,15 ?

        Thanks for your time

        • buf is always a pointer to an amount of data, which size is determined in USB settings. Commonly it’s 512 bytes. blk_len – is a number of 512 bytes data frames. So if you get blk_len = 2 in write() function you should write 1024 bytes.

          The block numeration is correct – from 0 to 15.

          If you’d like to organize a filesystem you should check if you have enough memory for it. Maybe 8 KBytes is too small…

          • I have an eeprom 24lc256. It has 32KBytes. There are 64 blocks of 512 bytes. That would be enough?

            So, if in write funcion parameters i have: blk_add = 5 and blk_len = 3, it means that i have to write 1536 bytes ( 512 in block 5 , 512 in block 6 and 512 in block 7). It’s that right?

            Thanks for your time

          • thank you very much for your help!! i will be continue working with this.

  4. I have build this rutine:

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

    char y[100];

    sprintf(&y[0],”%d”, blk_addr);
    LCD_Puts(0,0,&y[0]);

    int i,j,k=0;
    uint16_t a;
    uint8_t aux;
    a = blk_addr*512;

    for(i=0;i<blk_len;i++)
    {
    for(j=0;j<512;j++)
    {
    eeRead(a,&aux,1);
    buf[k]=aux;
    a++;
    k++;
    }
    }

    return (USBD_OK);
    /* USER CODE END 6 */
    }

    and other similar to write, the problem is that now, windows not recognize my device.

      • Hi, I have a problem with connecting device to computer. There is an info in device manager (on win10) that “request for the usb device descriptor failed”. I’m using SDIO 1bit mode (which works fine, because I can read/write files using FatFs). Could this 1bit mode be a reason why USB doesn’t work?
        I’ve done rest of things like above.

  5. Hi!

    I’m new to STM32 and STM32cube and I’m using a STM32F103C MCU without the SDIO peripheral. The generated code doesn’t contain diskio.c, only user_diskio.c with empty functions. I’ve searched for a SPI diskio drive and can only find ones for the STM32F4, which is not compatible with this smaller chip.

    Any ideas how to do this with SPI?

    • Hi!

      If there is no example with this driver for STM32F103, you should implement it yourself… You can take STM32F4 functions and rewrite them to work with STM32F103.

  6. hi Aveal
    tanx for your tutorial…
    i do just like above in my program with stm32f407vgt6 .
    but mass storage not recognized completely…
    my cube version(newer) differ with your version and in usbd_storage_if.c BLOCK_SIZE not define…
    i don’t know what’s the problem…Can you tell me?

  7. Thanks for the tutorial, may I ask how much is the maximum data transfer in this configuration? I mean how much time will it take to copy a file to pc in this method.

  8. Hi,
    Thanks for your helpful tutorial.
    I use cubemx 4.15.1. I active SD card with fatfs and USB msc in device mode for stm32f429. and the tips which you mentioned I also did in the programs. Besides, when I connect the board to PC which recognize it as usb device; however, there is no new drivers or files in my computer to show SD card.

  9. hello aveal,

    I am evaluating the STM32f4 discovery board. I want to implement the USB mass storage functionality.I interfaced the SD card using the SDIO 1-bit mode.But, I want to share SD card’s data in the PC use of USB Mass storage option.im able to read and write data to SD card using FATFS.but now i just want to implement USB part.

    thanks
    rakesh

      • thanks for your quick response aveal

        i followed your instruction.but still my usb is not detecting by my PC.i generated code using cubemx 4.12.0.i configured SDIO for 1 bit mode.sd card’s part was done by some one else. so i need to de USB part.so how to start with this i just did as you said.and i already have
        #define STORAGE_LUN_NBR 1
        #define STORAGE_BLK_NBR 0x10000
        #define STORAGE_BLK_SIZ 0x200.

        so should i again define BLOCK_SIZE?
        and what should i pass in lun?

        please help me.

        thanks

        • If STORAGE_BLK_SIZ is used in GetCapacity finction, you have no need to define BLOCK_SIZE. It is necessary in previous STM32CubeMx versions. All this functions are being called by USB driver automatically, so you don’t have to set lun manually.

          • hello sir i have tried all the solution given in this post but still my usb in not even getting detected by my PC.can we have usb mass storage functionality with sdio 1-bit?.i dont know what wrong with my code?

            please help me.

          • and one more thing sir my device is shows as mass storage in device and printers,and in device manager also but not in my computer.so what shoul i do now?is there may be problem with code?

          • yes
            sd card works properly.and i followed your steps only for usb.can i mail you my code for your better understanding?

          • hello sir,

            i have observed one thing that after calling bellow function
            void MX_USB_DEVICE_Init(void)
            {
            /* Init Device Library,Add Supported Class and Start the library*/
            USBD_Init(&hUsbDeviceFS, &FS_Desc, DEVICE_FS);
            USBD_RegisterClass(&hUsbDeviceFS, &USBD_MSC);
            USBD_MSC_RegisterStorage(&hUsbDeviceFS, &USBD_Storage_Interface_fops_FS);
            USBD_Start(&hUsbDeviceFS);
            }
            where USBD_MSC_RegisterStorage function is called but in this function argument &USBD_Storage_Interface_fops_FS is not calling the structure which is having function pointers.
            USBD_StorageTypeDef USBD_Storage_Interface_fops_FS =
            {
            STORAGE_Init_FS,
            STORAGE_GetCapacity_FS,
            STORAGE_IsReady_FS,
            STORAGE_IsWriteProtected_FS,
            STORAGE_Read_FS,
            STORAGE_Write_FS,
            STORAGE_GetMaxLun_FS,
            (int8_t *)STORAGE_Inquirydata_FS,
            };
            this is not called.

            so where may be the problem sir?

          • hello sir,

            i have already sent you a mail.
            sir after increasing my stack and heap size i got the drive in computer drive,but it is asking for format every time i try to open it.and at the time of
            formatting it is showing just 32 mb,in dick capacity.so what should i do now?please help me to solve this issue.

            thanks,

  10. hello sir,

    thanks a lot for your tutorial and your help,i got my usb mass storage working,but its taking too much time to detect the memory card.so how to solve this problem?

  11. Hi guy. In the stm32 cube mx 4.20 the genered code do not have HAL_SD_CardInfoTypedef and some function. will you fix your tutorial?

  12. Hello,
    Anyone resolve a problem with formatting 32MB when USB is connected to PC ? I’m using STM32L496AG with FatFS and CubeMX4.20.
    Can anyone help me with run the project, please i’m stuck 🙁

  13. Hi,
    Thank You for your effort, I made the project and it works very well.
    The problem now is the speed, how can I increase the speed of read/write, and I wounder why the speed is very low it’s about 200KB/s write and 600KB/s read ?
    Thanks

    • It’s common problem that Mass Storage Class in STM32 has slow speed… The best speed I saw I got with NAND memory and 16 bit FSMC and it was near 500 KB/s for writing.

      • I wounder if I Use High-speed core will the speed much increase or it will be a little bit different ?

        • I have the same problem, I try to transfer 1GB file and I got 350 KB/s during a
          write and 500 KB/s during a read. But I think DMA will be increase that speed.

          • The problem is that DMA is Memory -> peripheral, So how can I configure it to work with USB and SDIO ?

          • I increased data transfer to 1 MB/s by add 4096 data block to write but it’s all,.
            DMA not working for my project in new CubeMx – STM32L496 🙁 anyone can send me DMA SDIO example in new CubeMx ? Please 🙂
            this is my mail: johnrray5@gmail.com

  14. Hello Aveal,

    Thank you for this amazing tutorial! This is exactly what I was trying to do!

    Quick question, would it be feasible to get the file name that is currently being accessed?
    Again thank you so much!

    • Hello!

      It’s not a trivial task =) Maybe you can save sector numbers, where each file is located (while creating the files) and after that you can check which sectors are accessed during write operations. But I’m not sure it’s a good solution 🙂

  15. Hı Gays,
    I am working STM32F103C8T6 , but, STM32F103C8T6 isn’t SDIO,
    otherwise, ı have worked SD card over SPI and ı have achieved.
    but ı didnt work Mass storage class SD card with SPI ,

    this page ; extern SD_HandleTypeDef hsd;
    extern HAL_SD_CardInfoTypedef SDCardInfo; not for SPI because ıt is fall ,

    What are you doing this situation?
    Please help me 😉

    • Hello!
      You need to implement SD card driver yourself… Maybe it’s possible to change hardware part of SD driver to work with SPI instead of SDIO. The other opportunity is to search for official ST examples for SPI SD card.

      • Hı,
        maybe you dont understand me , ı already made them but I didnt just make Mass storage class SD card with SPI , would you like to share example links ?

        See you letter..

        • Hi!
          Unfortunately, I don’t have examples for SPI…
          As your SD driver is ready you should just rewrite STORAGE_Write_FS/STORAGE_Read_FS functions in order to make them work with your SPI SD-card functions.

          • Hı ,
            Please help me ,
            I just rewrite STORAGE_Write_FS/STORAGE_Read_FS functions in order to make them work with my SPI SD-card functions.
            but; ı dont make it,
            ın order to SPI this problem is solution please,

          • hı,

            My project send to mail ,
            Best Regards,

            thank you very much ,,,

  16. Hello;
    High appreciate for your help and your great tutorial. I have already done everything according to your tutorial. when i connect my SD card to computer, my computer knows mass storage device and also shows in my computer but i can not format it. is it related to codes?
    can you please help me and give me solution?
    best regards
    hamid

      • hi thanks for your answer, i am using sdio, i am using STM32F103RCTxxx, and MDK5. i write and configure all codes according to your tutorial , after loading on my mcu, and connect my board to computer, it shows the drive and even it shows the capacity of my sd card correct (8G) but hint to format for using driver, when i am going to format windows can not format the driver.
        i will appreciate if u can help me. i am using windows 7.

      • hello again, I have tested again and it is working. but here is another problem. When i put my sd card inside card reader and program my MCU, when i disconnect my Jtag and connect my board to pc, windows ask me to format my sd card, when i am going to format my sd card, windows can not format it, i went to disk manger and i found it there is no file system for my sd card, that means i have a SD card raw file system. so i did this way and it works, i format my sd card with my mobile phone and erase my stm32f103 and program and verified it again, then i put sd card inside slot and it starts to work without asking for format, when i disconnect and connect it again, it asked for format again but i cancel format and after several second it open my sd card and all files been there. sorry for writing too long, but i will appreciate if u can help me and give me some help. is it related to SDIOCLK clock divide factor because i set it 3 according to your tutorial. also speed of read and write is a bit slow.
        I hope you can help me because i spend to much time and it is not working properly .

        • Try to increase SDIO clock, but if the reason was there, it shouldn’t ask to format the card. Maybe you can send me your project?

        • Hello Hamid, I have been struggling with this problem for several weeks.
          Have been you success with this yet??? and how can you fix this.

  17. Hello Hesam
    it solved…
    the problem is heap size and stack size …
    put heap size=0x2000 and stack size=0x4000 and it works

  18. Again hı,

    I am using stm32f103c8t6, ı am working about STM32F103C8T6 sd card(fatfs)+usb mass storage over spı

    Please help me………

  19. Hi folks,

    I was trying to reproduce the project with STM32Cube V 4.22.0. I used the SDIO 1 bit and USB mass storage peripheral. Reading the SD capacity is going well but the read blocks function doesn’t respond as expected. When I’d a look deeper into the function I found out that I got an RX_OVERRUN error. Does somebody has the same issue? I’m looking for a way to solve this, are there any suggestions?

    Kind regards

    • Got it, just initialized a fifo buffer. But know when I try to read the first block of the SD with the function “HAL_SD_ReadBlocks” I get different data as when I read the SD card with WinHex on the computer. Both ways shows me the fat signature in the 510’th and 511’th bit but the data isn’t corresponding with each other. I search the whole SD card in WinHex to find the data which I got back from the “HAL_SD_ReadBlocks” function. But I wasn’t able to find it with WinHex. It looks strange to me, am I missing something important? The USB device which appear in windows still asked to be formatted. I’m working with the STM32F413. Cheers!

  20. Alright guys,

    Got it working. I’d to change the read function into
    HAL_SD_ReadBlocks(&hsd, buf, blk_addr, blk_len, 10);

    And the write function into
    HAL_SD_WriteBlocks(&hsd, buf, blk_addr, blk_len, 10);

    In combination with enlarging the heap and stack size and initialize the rx and tx buffers for the sdio it should work.

    Good luck.

Leave a Reply

Your email address will not be published. Required fields are marked *