How to reduce event delivery time - FreeRTOS

Hi, I have a requirement to perform an operation within 200us of receiving an interrupt. The ISR when triggered will post an event and once the event is delivered to the waiting task ,It works normal when only has one task(taskA) in the system.

When add the other task(taskB,this one is Lower priority,but takes about 5ms deal with some things), there is a delay in this event received by about 0.1~5 microseconds (random).

The TaskA has higher priority than the TaskA, why the TaskA can’t preempt the TaskB?

I am using this on STM32G473(Cortex M4) and have already used portYIELDFROM ISR() after xEventGroupSetBitsFromISR() in the ISR.

Thanks

Assuming this is a typo – and that your delay is currently 0.1 to 5.0 milliseconds not microseconds.

Can you post the ISR contents? Also, what is the value of configTICK_RATE_HZ?

#define configENABLE_FPU                         1
#define configENABLE_MPU                         0

#define configUSE_PREEMPTION                     1
#define configSUPPORT_STATIC_ALLOCATION          1
#define configSUPPORT_DYNAMIC_ALLOCATION         1
#define configUSE_IDLE_HOOK                      0
#define configUSE_TICK_HOOK                      0
#define configCPU_CLOCK_HZ                       ( SystemCoreClock )
#define configTICK_RATE_HZ                       ((TickType_t)1000)
#define configMAX_PRIORITIES                     ( 56 )
#define configMINIMAL_STACK_SIZE                 ((uint16_t)128)
#define configTOTAL_HEAP_SIZE                    ((size_t)20480)
#define configMAX_TASK_NAME_LEN                  ( 16 )
#define configUSE_TRACE_FACILITY                 1
#define configUSE_16_BIT_TICKS                   0
#define configUSE_MUTEXES                        1
#define configQUEUE_REGISTRY_SIZE                8
#define configUSE_RECURSIVE_MUTEXES              1
#define configUSE_COUNTING_SEMAPHORES            1
#define configUSE_PORT_OPTIMISED_TASK_SELECTION  0


ISR contents:

  if(LL_USART_IsActiveFlag_IDLE(USART3))
  {
    LL_USART_ClearFlag_IDLE(USART3);
    READ_REG(USART3->ISR);
    READ_REG(USART3->RDR);

    LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_8);
    LL_USART_DisableDMAReq_RX(USART3);

    Bus1.PhyConnect.RxLen=sizeof(Bus1.PhyConnect.RxBuf)- 
    LL_DMA_GetDataLength(DMA1,LL_DMA_CHANNEL_8);
    uint32_t xResult = osEventFlagsSet(myEvent01Handle, (1<<BUS1_RXED));
   if(xResult )
      ch26pin_toggle();
  }

TaskB contents:

osThreadId_t TaskB_TaskHandle;
const osThreadAttr_t TaskB_Task_attributes = {
  .name = "Bus1_Task",
  .priority = (osPriority_t) osPriorityNormal3,
  .stack_size = 256 * 4
};

void TaskB(void *argument)
{
  /* USER CODE BEGIN 5 */

  uint32_t bus1Flag;

  /* Infinite loop */
  for(;;)
  {
    bus1Flag = osEventFlagsWait(myEvent01Handle, (1<<BUS1_RXED), osFlagsWaitAny, osWaitForever);
    if(bus1Flag & (1<<BUS1_RXED))
    {
      ch25pin_toggle();
      //BusX_Packet_Parse(&Bus1);
    }
  }
  /* USER CODE END 5 */
}

TaskA contents:

osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {
  .name = "defaultTask",
  .priority = (osPriority_t) osPriorityNormal,
  .stack_size = 256 * 4
};

void StartDefaultTask(void *argument)
{
  /* USER CODE BEGIN 5 */
  Bus1TaskInit();
  /* Infinite loop */
  for(;;)
  {
    osDelay(10);
   Attitude_Calculation();//it takes about 5ms,can be Replaced with  **HAL_Delay(5);**
    
  }
  /* USER CODE END 5 */
}

you can see there is a delay betweent CH25 and CH26(A1~A2).

Thanks

What is osEventFlagsSet() doing? Is it a wrapper for a FreeRTOS event group? If so, that is a very inefficient way to unblock a task from an interrupt because event groups are not deterministic as you don’t know how many tasks setting an event bit will unblock. Because it’s not deterministic, FreeRTOS won’t perform the action in the task ISR itself, but instead defer the operation to the timer task. The configuration constants you show don’t include the timer task priority - but no matter what it is - the timer task will run before the task you are trying to unblock. Using a direct to task notification will be much faster.

Hi rtel,

it is defined in the cmsis_os2.c:

uint32_t osEventFlagsSet (osEventFlagsId_t ef_id, uint32_t flags) {
EventGroupHandle_t hEventGroup = (EventGroupHandle_t)ef_id;
uint32_t rflags;
BaseType_t yield;

if ((hEventGroup == NULL) || ((flags & EVENT_FLAGS_INVALID_BITS) != 0U)) {
rflags = (uint32_t)osErrorParameter;
}
else if (IS_IRQ()) {
#if (configUSE_OS2_EVENTFLAGS_FROM_ISR == 0)
(void)yield;
/* Enable timers and xTimerPendFunctionCall function to support osEventFlagsSet from ISR */
rflags = (uint32_t)osErrorResource;
#else
yield = pdFALSE;

if (xEventGroupSetBitsFromISR (hEventGroup, (EventBits_t)flags, &yield) == pdFAIL) {
  rflags = (uint32_t)osErrorResource;
} else {
  rflags = flags;
  portYIELD_FROM_ISR (yield);
}

#endif
}
else {
rflags = xEventGroupSetBits (hEventGroup, (EventBits_t)flags);
}

return (rflags);
}

It appears that the timer task priority is set lower than osPriorityNormal (24), so the timer task must wait for task A to finish Attitude_Calculation() – a 5ms operation. Only then can task B wake up, even though it has the highest priority of all.

As Richard suggests, using direct-to-task notification would resolve all of this, and your synchronization latency will end up even shorter than if you increase the priority of the timer task and stay with event groups.

Thank you !
it works as expected when set the timer task priority = 40.The delay is less than 30us.

i do a test with the notify, it still has a delay from 10us~1ms.

As Richard explained task notifications are the fastest way to signal an event to a task providing lowest latency and jitter. Do you properly use them following the example in the API docs ?
Especially using the xHigherPriorityTaskWoken flag ?

BaseType_t xHigherPriorityTaskWoken = pdFALSE;
vTaskNotifyGiveIndexedFromISR( xTaskToNotify, 0, &xHigherPriorityTaskWoken );
portYIELD_FROM_ISR( xHigherPriorityTaskWoken );

See also FreeRTOS task notifications, fast Real Time Operating System (RTOS) event mechanism for some common signaling mechanisms realized with task notifications.

One note about the event groups:

Event groups can be used without FreeRTOS timers (configUSE_TIMERS). One example from the FreeRTOS+TCP library: when an API waits for the IP-task, the function xEventGroupWaitBits() is called. When the IP-task has an answer, it will call xEventGroupSetBits() to wake up to signal user task.

Only when “FromISR” event group functions are called, the FreeRTOS timers feature (configUSE_TIMERS) might be needed.

Thank you for reminding me.

i used the API: void vTaskNotifyGiveFromISR( TaskHandle_t xTaskToNotify, BaseType_t *pxHigherPriorityTaskWoken )

So did it fix the excessive context switch time of 1 ms as expected when using task notifications ?

no, the 1ms still there.i will do more test about that.