Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

FreeRTOS: Why to call taskYIELD_FROM_ISR() method within the isrHandler

I try to understand why the user has to call the taskYIELD_FROM_ISR() method and why it isn't automatically called by the RTOS within the xStreamBufferSendFromISR method.

My question refers to the FreeRTOS_Manual p. 369.

/* A stream buffer that has already been created. */
StreamBufferHandle_t xStreamBuffer;

void vAnInterruptServiceRoutine( void ) {
  size_t xBytesSent;
  char *pcStringToSend = "String to send";
  BaseType_t xHigherPriorityTaskWoken = pdFALSE;

  /* Attempt to send the string to the stream buffer. */
  xBytesSent = xStreamBufferSendFromISR(xStreamBuffer,(void *) pcStringToSend,strlen( pcStringToSend),&xHigherPriorityTaskWoken);

  if(xBytesSent != strlen(pcStringToSend)){
    /* There was not enough free space in the stream buffer for the entire string to be written, ut xBytesSent bytes were written. */
  }

  /* 
    If xHigherPriorityTaskWoken was set to pdTRUE inside xStreamBufferSendFromISR() then a    
    task that has a priority above the priority of the currently executing task was unblocked 
    and a context switch should be performed to ensure the ISR returns to the unblocked task. 
    In most FreeRTOS ports this is done by simply passing xHigherPriorityTaskWoken into 
    taskYIELD_FROM_ISR(), which will test the variables value, and perform the context switch 
    if necessary. Check the documentation for the port in use for port specific instructions. 
  */

  taskYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

My understanding of the scenario

Preconditions

  • There are two tasks
    • Task1 (high priority) and Task2 (low priority)
  • Task1 is in Blocked-State waiting for a streamBuffer input
  • Task2 is in Running-State and is interrupted from vAnInterruptServiceRoutine

Within the ISR

  • The ISR method calls xStreamBufferSendFromISR()
  • This call means, that the Blocked-State of Task1 changes into the Ready-State

Case A If the ISR method is returning now, the scheduler will not be called and Task2 is running until the time slice period is over and then the scheduler switches to the high prior Task1.

Case B If the ISR method calls taskYIELD_FROM_ISR(xHigherPriorityTaskWoken); at last, the scheduler will be called and after returning the ISR, Task1 will be running instead of Task2.

Questions

  1. Is it right, that Task2 will be executed until a time slice period is over or is there another tigger for the task switch?
  2. Why is the taskYIELD_FROM_ISR() method not called automatically by the RTOS when calling xStreamBufferSendFromISR()?
like image 397
PascalS Avatar asked Oct 30 '19 17:10

PascalS


1 Answers

  1. Yes, without a call to taskYIELD_FROM_ISR, the scheduler gives context to Task1 on the next scheduled tick. This can be verified with Segger SystemView. uartPrintTask blocks on a semaphore and then prints data from a buffer. Notice the long delay between when uartPrintTask is "ready" and when it is "running". This delay is variable - although it never lasts longer than 1mS (the tick rate in the example) enter image description here

Now, the same example with taskYIELD_FROM_ISR added at the end of the ISR. uartPrintTask is consistently executed after the ISR enter image description here 2. FreeRTOS can't automatically call anything from an ISR. You've got full control over all interrupts. taskYIELD_FROM_ISR should be placed at the end of your ISR implementation (but you may have placed the call to xStreamBufferSendFromISR anywhere in the ISR).

One of the beautiful things about FreeRTOS (in my opinion) is that it doesn't hijack anything and gives tons of flexibility. You can have interrupts that execute completely "underneath" the RTOS - FreeRTOS doesn't need to know anything about them. Not automatically calling taskYIELD_FROM_ISR is another example of this flexibility (in my opinion).

From the SafeRTOS manual:

Calling either xQueueSendFromISR() or xQueueReceiveFromISR() within an interrupt service routine can potentially cause a task to leave the Blocked state which then necessitates a context switch if the unblocked task has a higher priority than the interrupted task. A context switch is performed transparently (within the API functions) when either xQueueSend() or xQueueReceive() cause a task of higher priority than the calling task to exit the Blocked state. This behavior is desirable from a task, but not from an interrupt service routine. Therefore, xQueueSendFromISR() and xQueueReceiveFromISR(), rather than performing the context switch themselves, instead return a value indicative of whether a context switch is required. If a context switch is required, the application writer can use taskYIELD_FROM_ISR() to perform the context switch at the most appropriate time, normally at the end of the interrupt handler. See “xQueueSendFromISR()” on page 69 and “xQueueReceiveFromISR()” on page 71 which describe the xQueueSendFromISR() and xQueueReceiveFromISR() functions respectively for more information.

like image 87
bamos Avatar answered Nov 17 '22 19:11

bamos