Skip to main content
Graduate
July 12, 2024
Question

Example Selector: FatFs_USBDisk -> FatFs_USBDisk_RTOS

  • July 12, 2024
  • 11 replies
  • 2480 views

I use NUCLEO-F767Z board and was working on a Application Framework that uses RTOS with different tasks, and logging to serial console and USBDisk. This is quite a while ago (STM32CubeIDE 1.6.1). I couldn't make USBDisk work with RTOS then. I switched to other tasks. But now I want to get back to this.

Now I see, that in STM32CubeIDE there is an example FatFs_USBDisk_RTOS. This might be what I need. But this example can only be selected for other evaluation boards.

Is it possible to port the example FatFs_USBDisk_RTOS to NUCLEO-F767Z for a newbee?

When I download the FatFs_USBDisk_RTOS example for a board which is supported, and look at the sources, then I can see that this is not using the API USBH_Init(), USBH_RegisterClass(), etc, .... - for integration with RTOS.
Therfore, I guess, there is no use trying to adapt and incorporate the FatFs_USBDisk reference into an RTOS project.
Will it be more promising to take FatFs_USBDisk_RTOS from another board and adapt that for NUCLEO-F767Z?

 

    This topic has been closed for replies.

    11 replies

    Super User
    July 12, 2024

    >Is it possible to port the example FatFs_USBDisk_RTOS to NUCLEO-F767Z for a newbee?

    If the newbee hires a Sherpa for the journey - definitely yes.

     

    FBerg.1Author
    Graduate
    July 13, 2024

    Let me ask differently:
    Can I use the API USBH_Init(), USBH_RegisterClass(), etc, .... in an RTOS project, when I take take, that only a single thread is using this API?

    Super User
    July 13, 2024

    Yes, these API should be protected from reentrancy, by calling them from a single thread for example. This is a necessary condition, but not sufficient.

     

    FBerg.1Author
    Graduate
    July 17, 2024
    • I created the demo project "FatFs_USBDisk" and tested it - it creates the STM32.TXT on the USB disk (ok).
    • I merged working code for serial console from another project into the "FatFs_USBDisk" demo and tested again:
      • serial console output is successfully generated (ok)
      • but the "FatFs_USBDisk" demo's main() routine calles USBH_Process(...) in a while(1) loop, and that does not return anymore (NOK)

    I looked for clashing use of MCU resources and found that both are using GPIO_PIN_8.
    The "FatFs_USBDisk" demo defines in Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_hal_rcc.c:

    #define MCO1_PIN GPIO_PIN_8

    and uses it in the same file here:

    void HAL_RCC_MCOConfig(uint32_t RCC_MCOx, uint32_t RCC_MCOSource, uint32_t RCC_MCODiv)
    {
     GPIO_InitTypeDef GPIO_InitStruct;
     /* Check the parameters */
     assert_param(IS_RCC_MCO(RCC_MCOx));
     assert_param(IS_RCC_MCODIV(RCC_MCODiv));
     /* RCC_MCO1 */
     if (RCC_MCOx == RCC_MCO1)
     {
     assert_param(IS_RCC_MCO1SOURCE(RCC_MCOSource));
    
     /* MCO1 Clock Enable */
     MCO1_CLK_ENABLE();
    
     /* Configure the MCO1 pin in alternate function mode */
     GPIO_InitStruct.Pin = MCO1_PIN; /* HERE! */
     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
     GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
     GPIO_InitStruct.Pull = GPIO_NOPULL;
     GPIO_InitStruct.Alternate = GPIO_AF0_MCO;
     HAL_GPIO_Init(MCO1_GPIO_PORT, &GPIO_InitStruct);
    
     /* Mask MCO1 and MCO1PRE[2:0] bits then Select MCO1 clock source and prescaler */
     MODIFY_REG(RCC->CFGR, (RCC_CFGR_MCO1 | RCC_CFGR_MCO1PRE), (RCC_MCOSource | RCC_MCODiv));
     }

    My code merged into this example for support of the serial console has added

    #define STLK_RX_Pin GPIO_PIN_8

    and uses it here:

    void HAL_UART_MspInit(UART_HandleTypeDef* huart)
    {
     GPIO_InitTypeDef GPIO_InitStruct = {0};
     if(huart->Instance==USART3)
     {
     /* USER CODE BEGIN USART3_MspInit 0 */
    
     /* USER CODE END USART3_MspInit 0 */
     /* Peripheral clock enable */
     __HAL_RCC_USART3_CLK_ENABLE();
    
     __HAL_RCC_GPIOD_CLK_ENABLE();
     /**USART3 GPIO Configuration
     PD8 ------> USART3_TX
     PD9 ------> USART3_RX
     */
     GPIO_InitStruct.Pin = STLK_RX_Pin|STLK_TX_Pin; /* HERE! */
     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
     GPIO_InitStruct.Pull = GPIO_NOPULL;
     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
     GPIO_InitStruct.Alternate = GPIO_AF7_USART3;
     HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
    
     /* USART3 DMA Init */
     /* USART3_TX Init */
     hdma_usart3_tx.Instance = DMA1_Stream3;
     hdma_usart3_tx.Init.Channel = DMA_CHANNEL_4;
     hdma_usart3_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
     hdma_usart3_tx.Init.PeriphInc = DMA_PINC_DISABLE;
     hdma_usart3_tx.Init.MemInc = DMA_MINC_ENABLE;
     hdma_usart3_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
     hdma_usart3_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
     hdma_usart3_tx.Init.Mode = DMA_NORMAL;
     hdma_usart3_tx.Init.Priority = DMA_PRIORITY_LOW;
     hdma_usart3_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
     if (HAL_DMA_Init(&hdma_usart3_tx) != HAL_OK)
     {
     Error_Handler();
     }
    
     __HAL_LINKDMA(huart,hdmatx,hdma_usart3_tx);
    
     /* USART3 interrupt Init */
     HAL_NVIC_SetPriority(USART3_IRQn, 5, 0);
     HAL_NVIC_EnableIRQ(USART3_IRQn);
     /* USER CODE BEGIN USART3_MspInit 1 */
    
     /* USER CODE END USART3_MspInit 1 */
     }
    
    }

     

    At least I found some clashing of using resources.
    Can that be resolved by mapping one or the other to a different MCU resource?
    (I don't know, if the wiring on the STM32F767ZI board offers that?!)
    Or can USB disk usage and output to serial console not be used in parallel at all?

    Super User
    July 17, 2024

    >At least I found some clashing of using resources.

    Which resources?

    FBerg.1Author
    Graduate
    July 19, 2024

    Hi Pavel,

    I am talking about both using the same GPIO_PIN_8 - for different purpose.

    However, when looking at data sheet

    https://www.st.com/resource/en/datasheet/stm32f777bi.pdf

    I wonder, if the Initialization for the USB Interface using

     

    #define MCO1_PIN GPIO_PIN_8

     

    for

     

     /* Configure the MCO1 pin in alternate function mode */
     GPIO_InitStruct.Pin = MCO1_PIN; /* HERE! */
     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
     GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
     GPIO_InitStruct.Pull = GPIO_NOPULL;
     GPIO_InitStruct.Alternate = GPIO_AF0_MCO;
     HAL_GPIO_Init(MCO1_GPIO_PORT, &GPIO_InitStruct);

     

    is correct?

    Because the serial console uses the same GPIO_PIN_8

     

    #define STLK_RX_Pin GPIO_PIN_8

     

    for

     

     GPIO_InitStruct.Pin = STLK_RX_Pin|STLK_TX_Pin; /* HERE! */
     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
     GPIO_InitStruct.Pull = GPIO_NOPULL;
     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
     GPIO_InitStruct.Alternate = GPIO_AF7_USART3;
     HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);

     

    which refers to https://www.st.com/resource/en/datasheet/stm32f777bi.pdf, page #91, Port B, PB11 for AF7.
    But on page #91 there is no entry for AF0 (what the USB interface is using.

    The MCO1 as AF0 (for the USB interface) is defined on another page #89, but for Port A, PA8.

    So a different port, and a different pin.

    How can both use #define for GPIO_PIN_8?

    Super User
    July 19, 2024

    GPIO_PIN_8 - on which port ? A, B, C, ... ? every port has a pin no. 8 , yes even more, like pin no. 7 or 9 ...

    RTFM .

    ... at least, a little bit. ok ?

    FBerg.1Author
    Graduate
    July 19, 2024

    Thanks @AScha.3

    I was wrong, thinking "GPIO_PIN_8" is using a global enumeration for all GPIO pins.

    So my talking about a conflict between USB interface and serial console  because of

     

    #define MCO1_PIN GPIO_PIN_8

     

    vs

    #define STLK_RX_Pin GPIO_PIN_8

    is obsolete.

    That's why@Pavel A. asked "Which resource?". But i didn't get it from that.

    Thanks!

    FBerg.1Author
    Graduate
    July 20, 2024

    With that I could debug better, and found out, that within USB_Process(...) for the case HOST_IDLE

     switch (phost->gState)
     {
     case HOST_IDLE :
    
     if ((phost->device.is_connected) != 0U)
     {
     USBH_UsrLog("USB Device Connected");
    
     /* Wait for 200 ms after connection */
     phost->gState = HOST_DEV_WAIT_FOR_ATTACHMENT;
     USBH_Delay(200U);
     (void)USBH_LL_ResetPort(phost);
    
     /* Make sure to start with Default address */
     phost->device.address = USBH_ADDRESS_DEFAULT;
     phost->Timeout = 0U;
    
    #if (USBH_USE_OS == 1U)
     phost->os_msg = (uint32_t)USBH_PORT_EVENT;
    #if (osCMSIS < 0x20000U)
     (void)osMessagePut(phost->os_event, phost->os_msg, 0U);
    #else
     (void)osMessageQueuePut(phost->os_event, &phost->os_msg, 0U, 0U);
    #endif
    #endif
     }
     break;

    the "USBH_Delay(200u)" is hanging.
    USBH_Delay(...) is just a wrapper for HAL_Delay(...). And it's hanging here:

    Program received signal SIGINT, Interrupt.
    WWDG_IRQHandler () at /home/frank/STM32CubeIDE/workspace_1.16.0/FatFs_USBDisk/SW4STM32/startup_stm32f767xx.s:110
    110 b Infinite_Loop
    bt
    #0 WWDG_IRQHandler () at /home/frank/STM32CubeIDE/workspace_1.16.0/FatFs_USBDisk/SW4STM32/startup_stm32f767xx.s:110
    #1 <signal handler called>
    #2 HAL_GetTick () at /home/frank/STM32CubeIDE/workspace_1.16.0/FatFs_USBDisk/Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_hal.c:303
    #3 0x08000f84 in HAL_Delay (Delay=Delay@entry=200) at /home/frank/STM32CubeIDE/workspace_1.16.0/FatFs_USBDisk/Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_hal.c:370
    #4 0x08000e00 in USBH_Delay (Delay=Delay@entry=200) at /home/frank/STM32CubeIDE/workspace_1.16.0/FatFs_USBDisk/Src/usbh_conf.c:451
    #5 0x08006032 in USBH_Process (phost=phost@entry=0x20002a54 <hUSBHost>) at /home/frank/STM32CubeIDE/workspace_1.16.0/FatFs_USBDisk/Middlewares/ST/STM32_USB_Host_Library/Core/Src/usbh_core.c:499
    #6 0x08000a10 in main () at /home/frank/STM32CubeIDE/workspace_1.16.0/FatFs_USBDisk/Src/main.c:164

    A default IRQ handler - which uses an endless loop - is in use.
    This

    1) blocks the main routine (and what it calls)
    2) does not increment the uwTick used by HAL_GetTick()

    So my use of the serial console broke the interrupt handler, which is used by the USB.

    Interestingly, it's not the setup for the serial console, that breaks the interrupt handler for USB.
    It's the 1st execution of
    HAL_UART_Receive(...) respectively HAL_UART_Transmit_DMA(...) for the serial console, which breaks it.

    See the code for merging support for serial console in the working "FatFs_USBDisk" example here: https://github.com/FBergemann/STM32-USBDiskAndConsole
    It's the use of TaskConsole_Run() in https://github.com/FBergemann/STM32-USBDiskAndConsole/blob/master/Src/main.c#L161 which breaks the USB interface. ( TaskConsole_Run() is invoking HAL_UART_Receive(...) and HAL_UART_Transmit_DMA(...). Don't wonder about the strange implementation of TaskConsole_Run(). It's taken from an RTOS project, which uses a task for this: https://github.com/FBergemann/STM32-FreeRTOS-Framework)

    Super User
    July 20, 2024
    WWDG_IRQHandler () at /home/frank/STM32CubeIDE/workspace_1.16.0/FatFs_USBDisk/SW4STM32/startup_stm32f767xx.s:110
    110 b Infinite_Loop

    For some reason the watchdog is enabled and it hits during some lengthy process (the 200 ms delay?)  

    Do you actually need the watchdog at this step of development? If yes, prevent it from triggering.

    USBH_Delay(...) is just a wrapper for HAL_Delay(...).

    With a RTOS, this is not great. Consider more RTOS friendly implementation of USBH_Delay.

     

    FBerg.1Author
    Graduate
    July 21, 2024

    Hi @Pavel A. 

    I locally added missing for the serial console via DMA:

    /**
     * @brief This function handles DMA1 stream3 global interrupt.
     */
    void DMA1_Stream3_IRQHandler(void)
    {
     /* USER CODE BEGIN DMA1_Stream3_IRQn 0 */
    
     /* USER CODE END DMA1_Stream3_IRQn 0 */
     HAL_DMA_IRQHandler(&hdma_usart3_tx);
     /* USER CODE BEGIN DMA1_Stream3_IRQn 1 */
    
     /* USER CODE END DMA1_Stream3_IRQn 1 */
    }

     I missed to adopt that from the reference projects (where it is available).

    The I debugged again and wondered about HAL_DMA_IRQHandler() in backtrace at a time, when I didn't expect it to be used.

    Is my implementation of TaskConsole_Run(...) here:

    https://github.com/FBergemann/STM32-USBDiskAndConsole/blob/master/SW4STM32/STM32F767ZI_Nucleo/Application/User/TaskConsole.c#L70

    ...wrong for just firing HAL_UART_Transmit_DMA(...) in lines #94, #99, and #101 - without waiting for confirmation from the DMA interrupt, that the job was done?
    I mean: Shouldn't I better lock a semaphore before calling HAL_UART_Transmit_DMA(...) and wait for it being released after HAL_UART_Transmit_DMA(...) - before continue to process?
    And that semaphore should be unlocked by the DMA1_Stream3_IRQHandler()?

     

    Another question:

    Is it correct to enable the IRQ globally in https://github.com/FBergemann/STM32-USBDiskAndConsole/blob/master/Src/main.c#L68 ?
    Or does that have to be enabled and disabled again around the invocations of HAL_UART_Transmit_DMA(...) ?