AnsweredAssumed Answered

In STM32F205 USB OTG HS doesn't work with tickless idle FreeRTOS

Question asked by Marcin Wesolowski on May 29, 2017

Dear Forum and ST Wizards,

I've spent few weeks so far researching problem with cubeMX USB app. Finally I found, that with tickless mode enabled, Windows 10 cannot enumerate the device, but with tickless disabled, everything works fine. It seems like a bug in the library or even hardware bug in STM32F205. I run FreeRTOS code with exactly the same configuration and tasks (but with old STD peripherial lib USB driver and app) on STM32F105VB and there's absolutely no problem with tickless mode and USB peripheral.

 

I suspect, that for some reason USB OTG HS IRQs don't wakeup MCU from sleep mode (after __wfi instruction), so some timings are not met and Windows surrenders with enumeration. But, interestingly, other interrupts do (like FreeRTOS'es systick or TIM6).

 

You can easily reproduce it on virtually any board with STM32F205 (I use VGT6 die rev 1) and USB OTG HS pins connected. Just create new cubeMX project, select the following config (tl;dr: USB OTG HS device only as CDC Virtual COM, FreeRTOS in tickless mode, SWD, HSE, heap 0x4000, everything else default afair).

 

Project configuration

Pinout tab:

- USB_OTG_HS (internal phy: device only)

- RCC (configure your clocks to achieve 120 MHz sysclock and 48 MHz for USB)

- SYS (I use SWD, so Debug = Serial Wire), Timebase source = TIM5 (because default Systick is for FreeRTOS)

- FREERTOS (enabled)

- USB_DEVICE (Class for HS IP: Communication Device Class Virtual Port)

 

Clocks tab:

Basically max. frequency allowed on each bus. I use ext crystal 24 MHz, so I selected:

- HSE as PLL input, M = /24, N = 240x, P = /2, Q = /5

- sysclock mux  = PLLCLOCK (no CSS)

- AHB prescaler = 1

- Cortex systimer prescaler = 1

- APB1 prescaler = 4

- APB2 prescaler = 2

 

Configuration tab:

- USB_DEVICE: everything default

  • FREERTOS:
    • Tasks and queues: create 2 tasks, priority Normal and Idle
    • Config - most of the entries left default, except from:
      • USE_PREEMPTION: enable
      • MAX_PRIORITIES: 4
      • MINIMAL_STACK_SIZE: 128 words
      • USE_TICKLESS_IDLE: Enable
      • Memory management scheme: heap_4
      • USE_TIMERS: Enable
      • LIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY: 3
    • Include parameters:
      • vTaskDelay: Enable
  • USB_OTG_HS:
    • Parameters tab (I think they're all default):
      • Speed (Full speed 12Mbps - the only available)
      • Endpoint 0 max packet size = 64 bytes
      • Enable IP DMA: Enabled
      • Physical interface: Internal Phy
      • Low power: disable (even with enabled the problem still occurs)
      • Use dedicated EP1 IRQ: Disabled
      • VBUS sensing: Disabled
      • Signal SoF: Disabled
    • NVIC Settings tab:
      • EP1 Out IRQ: Disabled
      • EP1 In IRQ: Disabled
      • OTG wake-up through EXTI 20: Enabled (grayed out anyway)
      • OTG Global interrupt: Enabled (grayed out anyway)
  • NVIC:
    • TIM5 & USB OTG priority = 3
    • Priority group = 4 bits for preemption (the only allowed for FreeRTOS)
    • all other stuff default values

 

Project settings:

- Minimum heap size 0x4000

- Minimum Stack size 0xa00

- Toolchain: TrueStudio

- Firmware package: STM32Cube FW_F2 V1.6.0 (but in 1.4.0 the problem also occured)

- Enable full assert

- my STM32cubeMX version: 4.21.0 (latest as of today)

 

Reproducing the error

Now generate project. Add to each task (inside the for(;;) loop) the following line:

osDelay(100);

There's also bug in the newest V1.6.0 library, so you have to manually add the following global array, otherwise project won't link:

const uint8_t APBPrescTable[8] = {0, 0, 0, 0, 1, 2, 3, 4};

Next, add some useful asserts to FreeRTOSConfig.h:

#define configASSERT( x ) if ((x) == 0) {taskDISABLE_INTERRUPTS(); __asm("bkpt"); }
#define configUSE_MALLOC_FAILED_HOOK              1
#define traceTIMER_CREATE_FAILED()                __asm("bkpt")
#define traceTASK_CREATE_FAILED()                 __asm("bkpt")
#define traceQUEUE_CREATE_FAILED( ucQueueType )   __asm("bkpt")

and update function void assert_failed(uint8_t* file, uint32_t line) in main.c with

__asm("bkpt")

When you run it wih tickless mode enabled, then after connecting USB cable (Win10 64) you get message, that device wasn't recognised (enumeration failed). After changing the following line in FreeRTOSConfig.h:

#define configUSE_TICKLESS_IDLE                  1

to

#define configUSE_TICKLESS_IDLE                  0

and rebuilding whole project (simple rebuild is not enough due to wrong TrueStudio project config), when you connect USB cable windows recognises device properly.

The funny thing is, that if you change this task with idle priority (as I did in the main project) to the following one:

  for(;;) {
  }

then the device is also recognised even with tickless enabled. It means, that when MCU does not go into sleep mode with __wfi instruction (which is done in tickless by FreeRTOS after both tasks call osDelay(100);), because one of the tasks is still busy, then all OTG interrupts work fine. If MCU goes to sleep, then ISRs are being run after MCU is woken up by another interrupt, like systick, but it occurs too rarely to meet USB timeouts.

 

However if you configure another IRQ source, eg. timer (like TIM6) to issue an update interrupt say each millisecond and blink LED, it will work properly in tickless mode, so this IRQ is not ignored and wakes up MCU. Along with that USB still doesn't work.

 

I checked BASEPRI reg right before executing __wfi (equals 0), similarly PWR_CR and PWR_CSR registers and both are 0, so it's not matter of putting MCU into deeper sleep modes.

 

Has anyone met with that problem? Dear ST Wizards, can you help?

 

Best regards,

Marcin

Outcomes