Skip to main content
Visitor II
June 2, 2020
Solved

Problem with USB Mass Storage Device class on STM32F401RBT6

  • June 2, 2020
  • 10 replies
  • 7899 views

Hi

My board has, among other things, an STM32F401RBT6 and a full-size USB B (device) connector. System clock is an external 25MHz TCXO.

I have verified that the hardware is working correctly, because I have already implemented a compound device class containing USB Audio device and Communications Device class and that all works perfectly.

When I try to flash a project containing a very simple MSC Device class with a 16K RAM Disk, nothing happens. Specifically I followed this ST Video tutorial: https://www.youtube.com/watch?v=GjQqZd1keBo which is very clear and he literally only writes/changes 5 lines of code. Yet when I plug in the USB cable to the PC I see nothing at all on the PC. I have tried on Linux, nothing shows up, even on an lsusb command typed in a terminal. I have tried on the only Windows machine I have, which is a WinXP Laptop. Again, nothing...

The video tutorial is written for a STM32F446 on a Nucleo board; the only changes I made compared to the video were:

1) Reduce the RAM disk size from 64K to 16K because my F401 processor has less memory than the F446

2) Different clock configuration as my processor is max 84MHz and my external clock TCXO is 25MHz. However the USB peripheral is correctly configured at 48MHz and processor clock at 84MHz etc so I do not believe there are any issues here.

I used "min heap size" 0x2000 and "min stack size" 0x400 as in the video.

Any help would be very much appreciated, I have googled and YouTubed for days and got nowhere, and all my hair is almost torn out...

Thanks in advance

Hans Summers

    This topic has been closed for replies.
    Best answer by HSumm.1

    Hello Simon

    Apologies for the delayed reply, we have a new baby boy in the family, born 1 week ago... and things have been rather hectic since then...

    Eventually I think I have concluded that the problems I had were not any fault in the ST libraries. My application project worked, but not my sample demo project which was to become my bootloader. I believe that in the end, this is my fault, for forgetting about a specific detail of my hardware.

    Specifically: my PCB (this is not an ST Dev board, it is my own PCB design) originally had a 25MHz crystal as the system clock, in a push-pull oscillator composed of an NPN and PNP transistor. It was done this way since the oscillator also acts as reference for an Si5351A PLL Synth chip. I did NOT choose to configure a MCO output of the STM32 since these have various low level spurii which would have been a problem in my application. Then I manufactured a batch of boards. AND THEN, I found I needed greater frequency stability. I replaced the crystal with a 25MHz TCXO which then has excellent stability; to do this I had to make a tiny daughterboard containing three connections (3.3V power, Gnd, and 25MHz output). The way it worked out, the only way to conveniently install this daughterboard was to make use of a spare I/O pin pad of the processor which happened to have been provided in my PCB layout, at a 0.1-inch header strip and was not in use. This spare I/O pin supplies 3.3V at 1.25mA for the TCXO. The I/O pin must be configured as GPIO output and set High by the GPIO_Init() before the system clock init is done. So that the TCXO is powered up. It's an inconvenient arrangement, obviously it would be much nicer to power the TCXO from the 3.3V supply rail but physically that did not work on my already-manufactured batch of 500 PCB Assemblies... that was why I opted to use the I/O pin which was conveniently placed.

    I believe my error was that in setting up the Mass Storage Device class according to the ST tutorial YouTube video, I followed the steps precisely; I even configured some other I/O pins which I knew I needed, to disable some parts of my hardware for the demo; BUT, I failed to remember that I needed to configure that I/O pin high, which powers my TCXO. Big doh.

    What I *ought* to have done of course, is try again the ST demo video, and this time remember to add the config to power up that I/O pin... but by the time I had it working and realized my error, after several frustrating weeks, I was so excited to continue and make progress that I just continued on with my next steps in my bootloader development!

    In the end then I have big fat piles of rotten eggs on my face... thanks to all who responded in this thread kindly, and actually on the plus side, all the debugging I did, I did learn a lot from. All the debugging showed a failure in the USB Core Reset function, a timeout of the command to the STM32 core to reset the USB peripheral; someone further up this thread suggested that would be likely due to incorrect clock configuration. In the end they are CORRECT, the ST library is not any longer under suspicion, it was, I believe, my own fault!

    So I progressed further and finished my USB Bootloader with a bit more blood, sweat and tears but no major blockages. What I have now is a bootloader which fits in the lowest 16K Flash sector of the STM32F401RBT6. When the bootloader is run in firmware update mode, my PCB appears to the host computer as a USB Flash drive, containing two files: the firmware binary, and an EEPROM file - which is read from the onboard I2C EEPROM chip. These two files can be read and written from/to the PCB exactly as one would read/write to a USB Flash drive; by copying files in File Manager. It works on Linux, Windows (I tested both) and presumably Mac, without any drivers, without any special software, etc. Because USB Flash drive MSC is already supported inherently by all Operating Systems. In the event that the user copies a new firmware file onto the board, the bootloader erases the upper Flash blocks (112K) and writes in the new binary, then boots into the newly updated application firmware. 256-bit AES encryption is also included which secures the firmware and board such that use of the firmware files on cloned PCBs is not possible, and installation of other firmware on my PCB is also not possible.

    All in all I am very pleased with the result.

    BUT, Simon, I am not sure that this helps at all with your problem. As I mentioned, in my own case after adding a 38400 baud USART and adding debug statements all over the place, I finally found the failure occurred in the USB_CoreReset() function. This should have been an indicator that the clock configuration was incorrect. You may have this problem, you may have others... I think there are a million ways to fail, and only one way to succeed... but this is not a life philosophy topic...

    Everything the kind members of this forum said was good advice and I would suggest you to also follow it; try adding debug statements to see what is going on inside the code. If it so happens that the USB_CoreReset() function is where it fails, just the same as in my case, then it would be worth examining the clock configuration to make sure you really do have 48MHz on the USB clock.

    Regards

    Hans

    10 replies

    Graduate II
    June 2, 2020

    Make sure HSE_VALUE is set correctly in stm32f4xx_hal_conf.h

    Add telemetry via a UART so you can understand the internal interactions, USB queries/responses, disk reads/writes, etc.

    HSumm.1Author
    Visitor II
    June 3, 2020

    Thanks Clive.

    HSE_Value is correctly set to 25MHz in stm23f4xx_hal_conf.h

    I will add telemetry as you suggest, and let you know if I can find anything...

    FYI the code generation used STCubeMX version 5.2.1. I believe that's a fairly recent version.

    Regards

    Hans

    HSumm.1Author
    Visitor II
    June 3, 2020

    Hi...

    An update...

    I implemented telemetry USART debugging at 38400 baud. I put a debug trace in EVERY function in all the USB source code files: usb_device.c, usbd_conf.c, usbd_desc.c, usbd_storage_if.c, usbd_msc_bot.c, usbd_msc_data.c, usbd_msc_scsi.c, usbd_msc.c, usbd_core.c, usbd_ctlreq.c, usbd_ioreq.c. as well as some additional status information in some functions, such as whether they return a success or fail.

    On booting the application, the following log is generated on the terminal, all of which occurs in the call to function MX_USB_DEVICE_Init(); in main().

    MX_USB_DEVICE_Init entry

    USBD_Init entry

    USBD_LL_Init entry

    HAL_PCD_MspInit entry

    HAL_PCD_MspInit exit

    USBD_LL_Init exit

    USBD_Init exit: OK

    USBD_RegisterClass entry

    USBD_RegisterClass exit

    USBD_MSC_RegisterStorage Entry

    USBD_MSC_RegisterStorage Exit

    USBD_Start entry

    USBD_LL_Start entry

    USBD_Get_USB_Status 0 OK 

    USBD_LL_Start exit

    USBD_Start exit OK

    MX_USB_DEVICE_Init exit

    On plugging in the USB cable to a PC, nothing at all happens. No new entries on the terminal. It feels like something very fundamental is wrong! But what? Until all this was added, the code was exactly like the ST training video except for the smaller number of blocks (to fit my smaller memory, 64K). As far as I know, NVIC interrupts are enabled properly. The physical connections are OK, it is not something silly like a defective USB cable, because if I load in my other application (with the compound USB device class containing audio and CDC) the computer recognizes it normally. There is not a hardfault because I am also flashing a LED in the main while loop at 1Hz. Nothing hangs up.

    Any suggestions would be very welcome

    Regards

    Hans

    Super User
    June 2, 2020

    nm

    Super User
    June 3, 2020

    If you probe the DM/DP lines, does one of them get pulled up to 3.3V?

    HSumm.1Author
    Visitor II
    June 3, 2020

    That's a very good point. I have not fitted a 1.5K pull-up resistor to D+ because the STM32F401 has this internally. I did actually leave a space on the PCB for one because I wasn't totally 100% convinced :) BUT - the other (main) application works fine. So that means there has to be something incorrect in the library set-up here, that has not enabled the STM32F401 internal pull-up...

    OK great, this narrows down the problem significantly... I will report back... thanks

    Hans

    HSumm.1Author
    Visitor II
    June 3, 2020

    Hi...

    Further update... I found that in the function USB_CoreReset() in stm32f4xx_ll_usb.c this code:

     do

     {

      if (++count > 200000U)

      {

       return HAL_TIMEOUT;

      }

     }

     while ((USBx->GRSTCTL & USB_OTG_GRSTCTL_CSRST) == USB_OTG_GRSTCTL_CSRST);

    produces a HAL_TIMEOUT.

    In other words the Core Reset bit of the GRSTCTL register is set by this function to cause a USB core reset, but the reset is not considered to have executed successfully.

    This error passes back up the call stack and prevents the proper initialization of the USB peripheral.

    Has anyone any idea, what could cause this?

    Thanks, Hans

    Super User
    June 3, 2020

    Have you clicked in VBUS support? If yes, is VBUS (PA9) connected?

    I don't Cube.

    JW

    HSumm.1Author
    Visitor II
    June 3, 2020

    JW... VBUS support is not clicked...

    Super User
    June 3, 2020

    > Core Reset bit of the GRSTCTL register is set by this function to cause a USB core reset, but the reset is not considered to have executed successfully.

    Sounds like some clock missing.

    Read out and check/compare to the working case the relevant RCC registers content.

    JW

    HSumm.1Author
    Visitor II
    June 4, 2020

    Hi JW

    The clock setup is indeed different between the working case and the non-working case. The situation in the working case is greatly complicated because I required a specific 512fs clock for 48ksps I2S which means 24.576MHz; a consequence is that the USB clock is not precisely 48MHz and neither is the CPU clock precisely 84MHz (it is 72.something). The USB clock is however, close enough to 48MHz to work fine. This is on the WORKING case which is quite a complex implementation, compound USB with Audio and CDC classes.

    What I was trying, to start with, with the current "problem" case is just the very simple example shown in the ST training video https://www.youtube.com/watch?v=GjQqZd1keBo - the project setup is all generated by Cube MX and the clock configuration is correct and exact (48MHz USB clock, 84MHz CPU, etc). The demo is very simple, there are only 3 lines of code (and 1 amended line). It's hard to imagine how anything could go wrong but...

    Next steps:

    1) My current installation dates from February this year. I note that ST have a one sub-version higher version of CubeMX and a version increment of the libraries too. So I have downloaded those and will try again to see if it is a bug somewhere in CubeMX and/or the Libraries and see if that was solved by the newer version.

    2) I will try setting up the peculiar clock scheme I have in my other, working project (note, both are on the exact same hardware, my own PCB design not an ST board).

    Thanks, Hans

    Super User
    June 4, 2020

    > The clock setup is indeed different between the working case and the non-working case.

    Okay but it's still simple to check whether the differences correspond only to the different source crystal, isn't it?

    Specifically, you should check whether PLL produces 48MHz at PLLQ, and whether the OTG_FS is enabled in its respective AxBENR. I'm not using specifically the 'F401 so maybe there's something else to be enabled/switched on, too, but the comparison with the working example should quickly reveal that.

    > What I was trying, to start with, with the current "problem" case is just the very simple example

    I know. Many people here say that. Expecting miracles is not the best strategy, even if miracles sometimes do happen (telling somebody about an already known defficiency somebody else has already painfully discovered, may seem like miracle, for example). USB is never simple. That clicking sometimes leads to working specimens is nice; but if it does not, you have to debug it exactly as you debug your own code (with the drawback that you have to develop understanding of somebody else's code in the process).

    JW

    Super User
    June 4, 2020

    I don't see how you can have USB working with a clock other than 48 MHz. That doesn't seem right.

    HSumm.1Author
    Visitor II
    June 4, 2020

    Hi TDK, JW

    The USB clock in my other (working) project with compound device class containing Audio and CDC, is 48.04 MHz which is within specification. The reason for that is that I need a 24.576 MHz clock (as close as possible) for 512 fs I2S audio CODEC sampling at 48 ksps. So I had to roll with a weird clock scheme what produces 48.04 MHz for the USB, 72.06 MHz for the CPU (HCLK) and 24.580 for the audio I2S master clock. Anyway that project WORKS...

    This one is supposed to be simple... but as JW says, that's expecting miracles and doesn't always work out that way. The beauty of using CubeMX and Libraries is that it gets you started quickly. The ugliness is when things don't work due to bugs etc then you end up in deep debug mode...

    FYI I tried again from scratch after upgrading CubeMX to the latest version 5.6.1, and upgrading STM32Cube for F4 to the latest 1.25.0 - all with the same result (fail).

    The hunt continues - but thanks to you guys I feel I'm getting closer to the prey...

    Hans

    Super User
    June 4, 2020

    > 48.04 MHz which is within specification

    USB2.0 requires the clock to be within 2500ppm i.e. 0.25% of specification; 48.04MHz deviates from 48MHz by 0.0832% - 832ppm, even adding conservative 100ppm for crystal error results in the clock being within limit

    But you could have achieved a similar setup while running system clock higher, almost at 84MHz, using PLLM=24 => VCOIN=24.576MHz/24=1.024MHz; PLLN=328 => VCOOUT = 335.872MHz; PLLP=4 (0b01) => SYSCLK = 83.968MHz, PLLQ=7 => Q = 47, 982MHz which deviates -0.0381% or 381ppm.

    A spredsheet to calculate these, here. Enjoy! =)

    JW

    HSumm.1Author
    Visitor II
    June 10, 2020

    Hi all

    Just an update of the situation so far...

    In summary - I can swap the firmware in the device (based on STM32F401RBT6) back and forth between my main project and this demo-project which I had hoped to evolve into a USB bootloader with the Mass Storage Device class. The main project implements compound USB device class containing an Audio device (stereo duplex 48ksps 24-bit) and a CDC serial communications port - and it works FINE. The bootloader project with MSC doesn't work - on the same hardware, with the same cables plugged in, nothing changed. Failure is in the USB_CoreReset function, where the processor never clears the reset bit that would indicate a successful reset of the USB peripheral.

    As per one of the suggestions, I changed the clock scheme to that of my main project, and still no joy.

    I also upgraded to the latest STCubeMX and F4cube (I was one minor version behind) and that made no difference either.

    I'm now assuming that there may be a library bug somewhere but have not been able to locate it. I did a Meld diff (Linux code differencing tool) on my working main project and on this demo MSC project. There were many differences since the original main project was written on a somewhat older version of Cube with slightly earlier HAL libraries and ST have made a *lot* of changes; however I could not find any differences which I think would be important. I did try changing a few things but still got no improvement on the issue; still the same timeout failure in USB_CoreReset.

    Now... I seem to have reached a dead end on this - so, since I have apparently needed to accumulate more low level USB knowledge than I ever thought I would need to have :\ I will try a different approach. I'm going to take a copy of my working main project and modify that. I believe I should be able to leave the low level stuff in place and modify just the top layers, removing my compound device, audio and CDC devices, and replacing that with Mass Storage Class. If that works then I can piece by piece strip out the other parts of the application to shrink it, and then customize it to be my bootloader.

    Hans

    HSumm.1Author
    Visitor II
    June 10, 2020

    Update...

    I ripped out of my application project, the Compound Device Class with Audio and CDC sub-classes, and instead put in the MSC from the CubeMX example. I had to make a few adjustments for some changes which occurred since my application project was created. Result: it worked, right away. My STM32F401RBT6 is now recognized as a 16KB Flash disk. Works on both Windows and Linux. Windows won't format it in FAT16 and neither will Linux (which says it needs 1MByte minimum) but this was not unexpected and is not going to be an issue in my application. I can now proceed with the next steps in the bootloader development!

    It would be nice to know what caused the supposedly simple CubeMX-generated MSC example to fail - presumably some kind of library issue - but I don't have time or inclination to dig into it any further. My project is onblocked and now I can continue, that's all that matters :)

    Many thanks for the support an encouragement!

    Hans

    Visitor II
    July 3, 2020

    Hi Hans,

    I have a very similar problem as yours (nothing shows up after the board is connected to PC) and struggled for a few weeks.

    Could you show me where should I modify?

    Thanks

    Simon

    Super User
    July 9, 2020

    Hi Hans,

    What a story! Thanks for sharing it.

    Please select your post as Best so that the thread is marked as solved.

    And all the best to the baby and his parents!

    Jan

    HSumm.1Author
    Visitor II
    July 9, 2020

    Thanks Jan

    And done! I selected my post as Best. I do consider the problem solved as far as I am concerned. I completed my USB bootloader firmware update project and it works beautifully.

    Regards, Hans

    Visitor II
    March 8, 2023

    I able to see the stmboard RAM as storage in pc.

    I need to transfer the data from RAM storage to pc in the form of files and buffer. how to transfer i unable to find it which function we need to use. can send me demo.

    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, &buffer1[blk_addr*STORAGE_BLK_SIZ], blk_len*STORAGE_BLK_SIZ);

     return (USBD_OK);

     /* USER CODE END 6 */

    }

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

    {

     /* USER CODE BEGIN 7 */

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

     return (USBD_OK);

     /* USER CODE END 7 */

    }