Skip to main content
crwper
Senior
February 12, 2020
Solved

Problems using USB mass storage middleware with BLE stack on STM32WB55?

  • February 12, 2020
  • 9 replies
  • 4612 views

I'm developing an application which uses both USB mass storage and BLE. I've started by building two separate applications using STM32CubeMX:

  • A USB mass storage application communicating with a microSD card over SPI1
  • A BLE P2P server

Both of these work fine on their own. But if I build an application that includes both components, the USB mass storage device will not enumerate properly.

Initially, I discovered that in the full application, HAL_PCD_IRQHandler got called, but it never fell through to PCD_EP_ISR_Handler. This makes me think the mass storage endpoints have not been configured properly.

I've also tackled the problem a different way--by systematically eliminating parts of the BLE initialization to determine what is interfering with USB mass storage. Using this method, I've been able to determine that if I comment out the call to SHCI_C2_BLE_Init in APP_BLE_Init (in app_ble.c), USB mass storage enumerates properly. If I then uncomment this line, it stops working again.

SHCI_C2_BLE_Init simply sends a message to CPU telling it to start the BLE stack, so this makes me think that the BLE stack on CPU2 is somehow interfering with USB endpoints configured before the call to APPE_Init in main.c.

I believe USB is a shared resource between CPU1 and CPU2, so it seems likely that the BLE stack is also trying to do something with USB. However, I'm at a loss as to how to debug this further or how to avoid the problem.

Any help would be greatly appreciated!

Michael

This topic has been closed for replies.
Best answer by crwper

Hi all--

I believe I've found a solution to this problem with the help of ST technical support. The underling issue is that CPU2 uses the USB clock to run the RNG. When it's done with the RNG, it will shut down the USB clock unless you've told it not to. The relevant code that solved the issue for me is this:

void PeriphClock_Config(void)
{
#if (CFG_USB_INTERFACE_ENABLE != 0)
 RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = { 0 };
 RCC_CRSInitTypeDef RCC_CRSInitStruct = { 0 };
 
 /**
 * This prevents the CPU2 to disable the HSI48 oscillator when
 * it does not use anymore the RNG IP
 */
 
 LL_HSEM_1StepLock( HSEM, 5 );
 
 LL_RCC_HSI48_Enable();
 
 while(!LL_RCC_HSI48_IsReady());
 
 /* Select HSI48 as USB clock source */
 PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_USB;
 PeriphClkInitStruct.UsbClockSelection = RCC_USBCLKSOURCE_HSI48;
 HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);
 
 /**
 * Configure the clock recovery system (CRS)
 */
 
 /* Enable CRS Clock */
 __HAL_RCC_CRS_CLK_ENABLE();
 
 /* Default Synchro Signal division factor (not divided) */
 RCC_CRSInitStruct.Prescaler = RCC_CRS_SYNC_DIV1;
 
 /* Set the SYNCSRC[1:0] bits according to CRS_Source value */
 RCC_CRSInitStruct.Source = RCC_CRS_SYNC_SOURCE_USB;
 
 /* HSI48 is synchronized with USB SOF at 1KHz rate */
 RCC_CRSInitStruct.ReloadValue = RCC_CRS_RELOADVALUE_DEFAULT;
 RCC_CRSInitStruct.ErrorLimitValue = RCC_CRS_ERRORLIMIT_DEFAULT;
 
 RCC_CRSInitStruct.Polarity = RCC_CRS_SYNC_POLARITY_RISING;
 
 /* Set the TRIM[5:0] to the default value*/
 RCC_CRSInitStruct.HSI48CalibrationValue = RCC_CRS_HSI48CALIBRATION_DEFAULT;
 
 /* Start automatic synchronization */
 HAL_RCCEx_CRSConfig(&RCC_CRSInitStruct);
#endif
 
 return;
}

This is placed in the "USER CODE BEGIN 4" block in main.c, and called from the "USER CODE BEGIN SysInit" block. The top half of the function prevents CPU2 from shutting down the USB clock. The second half is unrelated to the problem.

Michael

9 replies

crwper
crwperAuthor
Senior
February 12, 2020

I'm just taking another look at AN5289, and noticed in Table 2 (page 19) that USB is not listed as a shared peripheral. Am I wrong in assuming that USB is shared between CPU1 and CPU2?

Michael

crwper
crwperAuthor
Senior
February 22, 2020

I'm still working on this issue, and have been looking at USB initialization with and without the BLE stack started. If we don't start the BLE stack, the USB mass storage device enumerates normally and we see the following sequence of calls to HAL_PCD_IRQHandler:

HAL_PCD_IRQHandler: RESET 0 0
HAL_PCD_IRQHandler: ESOF 0 0
HAL_PCD_IRQHandler: WKUP 0 0
HAL_PCD_IRQHandler: RESET 0 0
HAL_PCD_IRQHandler: SOF ESOF 0 0
HAL_PCD_IRQHandler: ESOF 0 0
HAL_PCD_IRQHandler: ESOF 0 0
HAL_PCD_IRQHandler: ESOF 0 0
HAL_PCD_IRQHandler: ESOF 0 0
HAL_PCD_IRQHandler: ESOF 0 0
HAL_PCD_IRQHandler: ESOF 0 0
HAL_PCD_IRQHandler: ESOF 0 0
HAL_PCD_IRQHandler: ESOF 0 0
HAL_PCD_IRQHandler: ESOF 0 0
HAL_PCD_IRQHandler: ESOF 0 0
HAL_PCD_IRQHandler: ESOF 0 0
HAL_PCD_IRQHandler: ESOF 0 0
HAL_PCD_IRQHandler: ESOF 0 0
HAL_PCD_IRQHandler: ESOF 0 0
HAL_PCD_IRQHandler: ESOF 0 0
HAL_PCD_IRQHandler: ESOF 0 0
HAL_PCD_IRQHandler: SOF ESOF 0 0
HAL_PCD_IRQHandler: CTR 1 0
HAL_PCD_IRQHandler: CTR 0 0
HAL_PCD_IRQHandler: CTR 1 0
HAL_PCD_IRQHandler: RESET 0 0
HAL_PCD_IRQHandler: ESOF 0 0
HAL_PCD_IRQHandler: SOF ESOF 0 0
HAL_PCD_IRQHandler: SOF ESOF 0 0
HAL_PCD_IRQHandler: SOF 0 0
HAL_PCD_IRQHandler: SOF 0 0
HAL_PCD_IRQHandler: SOF 0 0
HAL_PCD_IRQHandler: SOF 0 0
HAL_PCD_IRQHandler: SOF 0 0
HAL_PCD_IRQHandler: SOF 0 0
HAL_PCD_IRQHandler: SOF 0 0
HAL_PCD_IRQHandler: SOF 0 0
HAL_PCD_IRQHandler: SOF 0 0
HAL_PCD_IRQHandler: SOF 0 0
HAL_PCD_IRQHandler: SOF 0 0
HAL_PCD_IRQHandler: SOF 0 0
HAL_PCD_IRQHandler: SOF 0 0
HAL_PCD_IRQHandler: SOF 0 0
HAL_PCD_IRQHandler: SOF 0 0
HAL_PCD_IRQHandler: CTR 0 0
HAL_PCD_IRQHandler: SOF 0 0
HAL_PCD_IRQHandler: SOF 0 0
HAL_PCD_IRQHandler: SOF 0 0
HAL_PCD_IRQHandler: SOF 0 0
HAL_PCD_IRQHandler: SOF 0 0
HAL_PCD_IRQHandler: CTR 1 0
HAL_PCD_IRQHandler: CTR 0 0
HAL_PCD_IRQHandler: CTR 1 0
HAL_PCD_IRQHandler: SOF 0 0
HAL_PCD_IRQHandler: SOF 0 0
HAL_PCD_IRQHandler: CTR 1 0
HAL_PCD_IRQHandler: CTR 0 0
HAL_PCD_IRQHandler: CTR 1 0
HAL_PCD_IRQHandler: CTR 1 0
HAL_PCD_IRQHandler: CTR 0 0
HAL_PCD_IRQHandler: CTR 1 0
HAL_PCD_IRQHandler: CTR 1 0
HAL_PCD_IRQHandler: SOF 0 0
HAL_PCD_IRQHandler: SOF 0 0
HAL_PCD_IRQHandler: SOF 0 0
HAL_PCD_IRQHandler: SOF 0 0
HAL_PCD_IRQHandler: CTR 0 0
HAL_PCD_IRQHandler: CTR 1 0
HAL_PCD_IRQHandler: SOF 0 0
HAL_PCD_IRQHandler: CTR 0 0
HAL_PCD_IRQHandler: CTR 1 0
HAL_PCD_IRQHandler: CTR 1 0
HAL_PCD_IRQHandler: SOF 0 0
HAL_PCD_IRQHandler: CTR 1 0
HAL_PCD_IRQHandler: CTR 1 0
HAL_PCD_IRQHandler: CTR 0 0
HAL_PCD_IRQHandler: SOF 0 0
HAL_PCD_IRQHandler: CTR 1 0
HAL_PCD_IRQHandler: CTR 0 0
HAL_PCD_IRQHandler: CTR 1 0
HAL_PCD_IRQHandler: CTR 1 0
HAL_PCD_IRQHandler: CTR 0 0
HAL_PCD_IRQHandler: CTR 1 0
HAL_PCD_IRQHandler: SOF 0 0
HAL_PCD_IRQHandler: CTR 0 0
HAL_PCD_IRQHandler: CTR 1 0
HAL_PCD_IRQHandler: CTR 1 0
HAL_PCD_IRQHandler: CTR 0 0
HAL_PCD_IRQHandler: SOF 0 0
HAL_PCD_IRQHandler: CTR 1 0
HAL_PCD_IRQHandler: SOF 0 0
HAL_PCD_IRQHandler: CTR 1 0
HAL_PCD_IRQHandler: CTR 0 1
HAL_PCD_IRQHandler: CTR 0 1
HAL_PCD_IRQHandler: CTR 0 1
HAL_PCD_IRQHandler: SOF 0 0
HAL_PCD_IRQHandler: SOF 0 0
HAL_PCD_IRQHandler: SOF 0 0
HAL_PCD_IRQHandler: SOF 0 0
HAL_PCD_IRQHandler: SOF 0 0
HAL_PCD_IRQHandler: SOF 0 0
HAL_PCD_IRQHandler: SOF 0 0
HAL_PCD_IRQHandler: SOF 0 0
HAL_PCD_IRQHandler: SOF 0 0
HAL_PCD_IRQHandler: SOF 0 0
... then more of this

Here, for each call to HAL_PCD_IRQHandler, I show the result of USB_ReadInterrupts, i.e., what flags are set in USB_ISTR. The last two fields show the value of DIR and EP_ID.

(continued...)

crwper
crwperAuthor
Senior
February 22, 2020

(...continued)

If, instead, we allow the BLE stack to start by calling SHCI_C2_BLE_Init, here is the result:

HAL_PCD_IRQHandler: RESET 0 0
HAL_PCD_IRQHandler: ESOF 0 0
HAL_PCD_IRQHandler: RESET 0 0
HAL_PCD_IRQHandler: SOF 0 0
HAL_PCD_IRQHandler: ESOF 0 0
HAL_PCD_IRQHandler: ESOF 0 0
HAL_PCD_IRQHandler: SUSP ESOF 0 0
HAL_PCD_IRQHandler: WKUP 0 0
HAL_PCD_IRQHandler: ESOF 0 0
HAL_PCD_IRQHandler: ESOF 0 0
HAL_PCD_IRQHandler: ESOF 0 0
HAL_PCD_IRQHandler: SUSP ESOF 0 0
HAL_PCD_IRQHandler: WKUP 0 0
HAL_PCD_IRQHandler: ESOF 0 0
HAL_PCD_IRQHandler: ESOF 0 0
HAL_PCD_IRQHandler: ESOF 0 0
HAL_PCD_IRQHandler: SUSP ESOF 0 0
HAL_PCD_IRQHandler: WKUP 0 0
HAL_PCD_IRQHandler: ESOF 0 0
HAL_PCD_IRQHandler: ESOF 0 0
HAL_PCD_IRQHandler: ESOF 0 0
HAL_PCD_IRQHandler: SUSP ESOF 0 0
HAL_PCD_IRQHandler: WKUP 0 0
HAL_PCD_IRQHandler: ESOF 0 0
HAL_PCD_IRQHandler: ESOF 0 0
HAL_PCD_IRQHandler: ESOF 0 0
HAL_PCD_IRQHandler: SUSP ESOF 0 0
HAL_PCD_IRQHandler: WKUP 0 0
HAL_PCD_IRQHandler: ESOF 0 0
HAL_PCD_IRQHandler: ESOF 0 0
HAL_PCD_IRQHandler: ESOF 0 0
HAL_PCD_IRQHandler: SUSP ESOF 0 0
HAL_PCD_IRQHandler: WKUP 0 0
HAL_PCD_IRQHandler: ESOF 0 0
HAL_PCD_IRQHandler: ESOF 0 0
HAL_PCD_IRQHandler: ESOF 0 0
HAL_PCD_IRQHandler: SUSP ESOF 0 0
HAL_PCD_IRQHandler: WKUP 0 0
... then more of this

In this case, the mass storage device goes into a sequence of suspending after 3 ESOF interrupts, then waking up, receiving 3 more ESOF interrupts, suspending, etc.

This is a very simple application. I've just built a minimal BLE server and USB mass storage device using the ST middleware. Individually, each one works fine. But if the BLE stack is allowed to start, the USB mass storage device fails to enumerate.

At this point, I'm trying to understand what might cause the BLE + MS version to go into a loop of suspending and waking up. I'm hoping some of the information above might mean more to one of you.

Any ideas would be greatly appreciated. Thanks for your help!

Michael

~Amit~
Associate III
February 26, 2020

Hi Michael,

I am facing the same issue here...

took me about a few hours just to understand there is a correlatrion between the USB and the BLE :weary_face:

did u solve it?

~Amit~
Associate III
February 26, 2020

tried to replace the initialization order, that helped once but not stable

crwper
crwperAuthor
Senior
February 26, 2020

Hi Amit--

I haven't figured it out yet, but am still exploring the problem. I'll post any progress I make here. Please let me know if you learn anything new.

Michael

crwper
crwperAuthorAnswer
Senior
March 4, 2020

Hi all--

I believe I've found a solution to this problem with the help of ST technical support. The underling issue is that CPU2 uses the USB clock to run the RNG. When it's done with the RNG, it will shut down the USB clock unless you've told it not to. The relevant code that solved the issue for me is this:

void PeriphClock_Config(void)
{
#if (CFG_USB_INTERFACE_ENABLE != 0)
 RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = { 0 };
 RCC_CRSInitTypeDef RCC_CRSInitStruct = { 0 };
 
 /**
 * This prevents the CPU2 to disable the HSI48 oscillator when
 * it does not use anymore the RNG IP
 */
 
 LL_HSEM_1StepLock( HSEM, 5 );
 
 LL_RCC_HSI48_Enable();
 
 while(!LL_RCC_HSI48_IsReady());
 
 /* Select HSI48 as USB clock source */
 PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_USB;
 PeriphClkInitStruct.UsbClockSelection = RCC_USBCLKSOURCE_HSI48;
 HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);
 
 /**
 * Configure the clock recovery system (CRS)
 */
 
 /* Enable CRS Clock */
 __HAL_RCC_CRS_CLK_ENABLE();
 
 /* Default Synchro Signal division factor (not divided) */
 RCC_CRSInitStruct.Prescaler = RCC_CRS_SYNC_DIV1;
 
 /* Set the SYNCSRC[1:0] bits according to CRS_Source value */
 RCC_CRSInitStruct.Source = RCC_CRS_SYNC_SOURCE_USB;
 
 /* HSI48 is synchronized with USB SOF at 1KHz rate */
 RCC_CRSInitStruct.ReloadValue = RCC_CRS_RELOADVALUE_DEFAULT;
 RCC_CRSInitStruct.ErrorLimitValue = RCC_CRS_ERRORLIMIT_DEFAULT;
 
 RCC_CRSInitStruct.Polarity = RCC_CRS_SYNC_POLARITY_RISING;
 
 /* Set the TRIM[5:0] to the default value*/
 RCC_CRSInitStruct.HSI48CalibrationValue = RCC_CRS_HSI48CALIBRATION_DEFAULT;
 
 /* Start automatic synchronization */
 HAL_RCCEx_CRSConfig(&RCC_CRSInitStruct);
#endif
 
 return;
}

This is placed in the "USER CODE BEGIN 4" block in main.c, and called from the "USER CODE BEGIN SysInit" block. The top half of the function prevents CPU2 from shutting down the USB clock. The second half is unrelated to the problem.

Michael

FPro.1
Associate
March 5, 2020

I had the same problem. I want to access the MCU over USBserialCOM-Port or BLE. After the scheduler was called, the controller is not reachable over USB anymore. I added the code you provided and it works perfectly fine again. I did not experience this problem with the Nucleo-Board. Maybe because I used a different BLE-Stack-Firmware back then.

Thank you very much. :)

BNist.1
Associate III
May 10, 2020

Hello! I know this is not really related to your issue, but I've noticed that you managed to make an microSD work with SPI on STM32WB55.

Would you mind sharing the code that allowed you to do this? I am literally doing the same functions as you did, but I don't seem to make the SD card work by any means... It always returns FR_NOT_READY..

Thanks!

crwper
crwperAuthor
Senior
May 12, 2020

Of course! The attached project should work on a P-NUCLEO-WB55 board. The microSD card is connected as shown here:

0693W000000XK3pQAG.png

To build this project, I started with an empty P-NUCLEO-WB55 project, added SPI1 and FATFS, configured both, then added/replaced these files in the FATFS/Target folder:

  • mmc.c
  • mmc.h
  • user_diskio.c

My disk interface (build around ChaN's example) is contained in mmc.* and these functions have been added to the original skeletons in user_diskio.c.

Note that I'm not using SPI very efficiently here. Even for large transfers to the microSD card, I'm using polling HAL functions in mmc.c. To optimize, you should use DMA transfers in the "rcvr_spi_multi" and "xmit_spi_multi" functions.

On the hardware side, you'll need pull-ups on the NSS and MISO lines. I think I'm using 10k.

Michael

BNist.1
Associate III
May 13, 2020

Hey! Thank you so much for your answer!

I have some questions for you if you have the time. These are the steps that I took:

  • I used your files and modified the CS pin in order to be the same as mine PA15
  • I added "Hardware NSS output signal" in the SPI section. Here I don't quite understand this and what is a NSS. I have a module SD which has my CS on it, I don't quite get why you suggested using NSS.(or if I even understood it well enough)Then I changed the PIN back to GPIO_OUT. In both cases the program is doing the same thing:
  • I then ran the program with debugging on "f_mount" and "f_open", however when it reaches "f_mount" it does not go further on the the other instructions and it keeps running. When I pause it the first instruction in the thread is the init_spi from "mmc.c" with the timers:
void init_spi (void)
{
	CS_HIGH();			/* Set CS# high */
 
	for (Timer1 = 10; Timer1; ) ;	/* 10ms */
}

I am kind of lost here, there are no errors, no warnings, it just keeps running. (I have a pressure sensor connected as well and the system works as I get data from the sensor, however the SD won't get past the f_mount)

You also said about having pull-ups on the NSS and MISO lines. I have no pull-ups, I thought that the SD module was taking care of all of that. Do I still need to add them? And when you say NSS, you mean the CS pin?

Sorry if all of these sound like basic questions and thanks again!

BNist.1
Associate III
May 22, 2020

Hello again!

I finally figured it out, I had forgotten to write the interrupt for the mmc file and that is why it was not working! I took me a while to get a grip of it, but I am glad I can finally transfer my data to the SD card!

Now I bumped into another problem, as I am trying to advance the project and send the sensor data via BT to my phone, while also registering it at the same time to the SD card.

It is exactly the same issue you've had. Both applications are working perfectly on their own, but after introducing the code for the SD card nothing works anymore.

I don't get this exactly, but I am trying to set the clock to PLLCLK in order to use the peripherals, but at this point the BT is not working anymore (neither does the SD but this is another thing). I have tried introducing your code that you mentioned above, but it doesn't seem to have any effect. I am definitely approaching this from a wrong perspective.

What else did you change in your code in order to be able to receive sensor data by BT while also storing it via SD? Is there any call function to the code you shared?

How do I deal with the clock in this situation, I saw on the Hands On tutorial that the BT uses HSE, however the SD would use PLLCLK (from what I understand), how do I merge them in order to work together.

Let's say the BT works, however I have my UTIL_SEQ_Run(~0); that starts the BT functions. This would be placed in the main.c function, however so is my code for the SD card. How would I get past this in order to access the rest of the code for the SD? Would I write this one in the template_stm? I also seem to have a problem with the MMC_timerproc handler from stm32wbxx_it.c. When I keep this function there, the BT stops working going into some SPI function.

I hope you could help me with this and thank you for your time!

Bogdan

crwper
crwperAuthor
Senior
May 26, 2020

It sounds like there are a few gaps in your understanding which are causing problems here. I would approach it from this angle:

First, check out the STM32WB Workshop here:

https://www.st.com/content/st_com/en/support/learning/stm32-education/stm32-moocs/STM32WB_workshop_MOOC.html

Don't just read it, though. Follow along with each step, and make sure you understand exactly why they're doing each one, specifically what the code does, etc. It's easy to overlook opportunities to learn if you're just copying and pasting code.

Next, take a look at this application note from ST:

https://www.st.com/content/ccc/resource/technical/document/application_note/group1/43/ea/2f/dc/10/a3/46/e6/DM00598033/files/DM00598033.pdf/jcr:content/translations/en.DM00598033.pdf

Again, if you haven't already done so, carefully read the whole thing. This document does a great job of expanding on what's done in the STM32WB Workshop.

Next, take a close look at the BLE_HeartRate example. Don't just compile it and install it. Notice how simple the main loop is. This example does a lot of what you're trying to do (minus the SD interface). So where are they generating "sensor" data? How are they handling low power modes?

Finally, read the RCC chapter in the Reference Manual. The STM32 has a clock tree which generates a lot of different clocks for different peripherals. It's worth reading closely so you understand how each of these clocks is generated, what peripherals they're associated with, and what happens to each one when you go into a low power mode.

I hope this helps!

Michael

BNist.1
Associate III
May 28, 2020

Hello,

Thank you so much for this! I have had already done the MOOC, and I would say that I understand most of it. Now I managed to make the sensors work and to transmit them to the ST BLE Sensor app. However I can only have one screen active at a time. So for instance I can either have the temperature or humidity transmitted at a time, but not both at the same time.

I have created a custom template for both of them, and as I said separately they work fine, however I can't figure out how to have both of them at the same time.

Each template has its own UUID, specific to temperature and humidity, however I don't really understand this portion of the code:

/**
 * Advertising Data
 */
#if (P2P_SERVER1 != 0)
static const char local_name[] = { AD_TYPE_COMPLETE_LOCAL_NAME ,'T','R','Y','P','I'};
uint8_t manuf_data[14] = {
 sizeof(manuf_data)-1, AD_TYPE_MANUFACTURER_SPECIFIC_DATA, 
 0x01/*SKD version */,
 CFG_DEV_ID_P2P_SERVER1 /* STM32WB - P2P Server 1*/,
 0x00 /* GROUP A Feature */,
 0x04 /* GROUP A Feature */, //0x00040000
 0x00 /* GROUP B Feature */,
 0x00 /* GROUP B Feature */,
 0x00, /* BLE MAC start -MSB */
 0x00,
 0x00,
 0x00,
 0x00,
 0x00, /* BLE MAC stop */
};
#endif
/**

My transmission works only when I change the mask to 0x00040000 for temperature (the temperature screen in the app and the transmission work fine) or when I change it to 0x00080000 for humidity. How would I manage to have both of them at the same time?

I read the documents that you have sent in their entirety, also I checked the example apps, but I can't seem to figure it out.

Thank you,

Bogdan

Edit: I forgot to mention that this portion of the code is from app_ble.c