Chapter 19

USING the FreeRTOS API function calls from an ISR

Abstract

There are applications where we may want to call the FreeRTOS functions from inside an ISR. It is important to realize that most of the FreeRTOS functions are not valid inside an ISR. If a FreeRTOS function is called from an ISR, then it is not being called from a task. FreeRTOS provides two versions of some of the functions where one is for use from ISRs. The functions that can be used inside an ISR have the word FromISR appended to their names. It is recommended that a FreeRTOS function must not be called from an ISR unless its name is appended with the word FromISR. This chapter gives several real time FreeRTOS based multitasking projects and describes in detail how to use task related ISR functions, event group related ISR functions, timer related ISR functions, and the semaphore related ISR functions.

Keywords

FreeRTOS
Clicker 2 for STM32
mikroC Pro for ARM
task priorities
deferred interrupt processing
ISR (interrupt service routine)
task related FreeRTOS ISR functions
event group related FreeRTOS ISR functions
timer related FreeRTOS ISR functions
semaphore related FreeRTOS ISR functions

19.1. Overview

In the last chapter we have learned how to use external and internal interrupts in our mikroC Pro for ARM based programs. In this chapter we shall learn how to call the FreeRTOS API functions from an ISR.
There are applications where we may want to call the FreeRTOS functions from inside an ISR. It is important to realize that most of the FreeRTOS functions are not valid inside an ISR. If a FreeRTOS function is called from an ISR, then it is not being called from a task. FreeRTOS provides two versions of some of the functions where one is for use from inside ISRs, and the other one for use outside ISRs. The functions that can be used inside an ISR have the word FromISR appended to their names and these are known as interrupt safe functions. It is recommended that a FreeRTOS function must not be called from an ISR unless its name is appended with the word FromISR.
As suggested by the FreeRTOS developers, there are some benefits of using separate interrupt safe functions. Firstly, using a separate function for use in interrupts makes both the task code and the ISR more efficient. There are also some disadvantages of using a separate interrupt safe function, usually when integrating third party code in our programs. Detailed explanations of the advantages and disadvantages of using separate FreeRTOS functions in ISRs is explained in the following references:
Mastering the FreeRTOS Real Time Kernel: A Hands on Tutorial Guide, by Richard Barry, link:
And
The FreeRTOS Reference Manual: API Functions and Configuration Options, link:
Also the FreeRTOS web site:

19.2. The xHigherPriorityTaskWoken parameter

It is highly likely that if a context switching is done by an interrupt, then the task that runs after the interrupt exists may be different to the task that was running when the interrupt occurred. Consider the case where a task is in Blocked state. If this task gets unblocked by a FreeRTOS function call and if the priority of this task is higher that the task which is in the Running state, then the scheduler is expected to switch to the higher priority task. In fact, the algorithm to switch to the higher task is dependent whether the API function is called from a task or from an ISR.
If the API function was called from a task and if preemptive scheduling is enabled, then the scheduler automatically switched to the higher priority task, as this is the normal state of affairs with tasks running in FreeRTOS.
If on the other hand the API function was called from an ISR, switch to a higher priority task will not occur automatically inside the ISR. Instead, a variable called *pxHigherPriorityTaskWoken is set to pdTRUE by the interrupt safe API functions to indicate that a context switch should be performed. This variable must be initialized to pdFALSE before it is used so that its change can be detected. If the program developer does not want a context switch from the ISR then the higher priority task will remain in the Ready state until the scheduler runs on the next tick interrupt. As described in length in the above references, there are several reasons why context switching does not occur automatically inside an ISR. Some of the reasons are: avoiding unnecessary context switches, portability, efficiency, control of the execution sequence, etc.
The macros portEnd_SWITCHING (or portYIELD_FROM_ISR) are the interrupt safe versions of the taskYIELD() macro. The xHigherPriorityTaskWoken parameter is passed to these macros. If xHIGHERPriorityTaskWoken is pdFALSE then context switching does not occur. If on the other hand pdTRUE then a context switching is requested and the task in the Running state may change, even though the interrupt always returns to the task in the Running state.

19.3. Deferred interrupt processing

It is always advisable to keep an ISR as short as possible so that very little time is spent in the ISR. If the ISR is going to be long, then it is advisable to record the cause of the interrupt and do the main interrupt processing outside the ISR. This is called Deferred Interrupt and is a commonly used technique. For example, suppose that we have an event counter program where the occurrence of events causes an external interrupt to occur where a variable is incremented each time an interrupt occurs. We may then want to display the total count on an LCD. In such a case it is best only to increment the variable inside the ISR and then perform the display functions outside the ISR and inside a task. Deferring an interrupt processing makes the program more efficient also makes it easier to manage the priorities in a program. If the task where the interrupt is deferred has the highest priority in the system then it is guaranteed that it will run after returning from the interrupt. Additionally, by deferring an interrupt processing, we can use all of the FreeRTOS API functions safely inside a task.
In the next few sections of this chapter we shall be looking at some of the FreeRTOS API functions that can be called safely from ISRs. Further information on these functions can be obtained from the references given in the Overview section of this chapter.

19.4. Task related functions from ISR

Some task related functions are given in this section. Full details can be obtained from the references given in the Overview section of this chapter.

19.4.1. taskENTER_CRITICAL_FROM_ISR() and taskEXIT_CRITICAL_FROM_ISR()

These are the interrupt safe versions of the API functions taskENTER_CRITICAL() and taskEXIT_CRITICAL() that are used to implement critical sections. taskENTER_CRITICAL_FROM_ISR() returns The interrupt mask state at the time of the call. The return value must be saved so it can be passed into the matching call to taskEXIT_CRITICAL_FROM_ISR() which does not return any value.

19.4.2. xTaskNotifyFromISR()

This is the interrupt safe version of the function xTaskNotify(). The format of the function is:
  • xTaskNotifyFromISR(TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eActionBaseType_t *pxHigherPriorityTaskWoken);
The parameters are same as function xTaskNotify() except the last parameter which has the following definition:
  • pxHigherPriorityTaskWoken: This parameter must be initialized to pdFALSE before being used. It is set to pdTRUE if the task being notified has a priority above that of the currently running task. A context switching should be requested if the parameter is set to pdTRUE.

19.4.3. xTaskNotifyGiveFromISR()

This is the interrupt safe version of the function xTaskNotifyGive(). The format of the function is:
  • vTaskNotifyGiveFromISR(TaskHandle_t xTaskToNotify, BaseType_t *pxHigherPriorityTaskWoken);
The parameters are same as function xTaskNotify() except the *pxHigherPriorityTaskWoken which is described above.

19.4.4. xTaskResumeFromISR()

This is the interrupt safe version of the function xTaskResumeFromISR(). The format of the function is:
  • xTaskResumeFromISR(TaskHandle_t pxTaskToResume);
The function has only one parameter which is the handle of the task to be resumed. The function returns the following values:
  • pdTRUE: The task being resumed (unblocked) has a priority equal to or higher than the currently executing task (the task that was interrupted), therefore a context switch should be performed before exiting the interrupt.
  • pdFALSE: The task being resumed has a priority lower that the currently executing task, therefore there is no need to perform context switching

19.5. Project 48-Using function xTaskResumeFromISR()

Description: In this simple project two tasks are created. Task 1 flashes the LED connected to port pin PE12 of the clicker 2 for STM32 development board. Task 2 configures an external interrupt on port pin PA0. As soon as Task 1 runs it suspends itself (i.e., moves to Blocked state). When the button is pressed it creates an external interrupt. Inside the ISR Task 1 is resumed so that the LED starts flashing.
Aim: The aim of this project is to show how the interrupt safe function xTaskResumeFromISR() can be used in a program.
Block diagram: The block diagram of the project is shown in Fig. 19.1. The push-button switch is connected to port pin PA0 such that normally its state is at logic LOW and goes to logic HIGH when pressed.
image
Figure 19.1 Block diagram of the project.
Program listing: The program listing of the project (program: isrresume.c) is shown in Fig. 19.2. There are two tasks in the program in addition to the Idle task, and both tasks are configured to run at the same priority. The operation of the two tasks and the ISR are described by the following PDLs:
image
image
Figure 19.2 isrresume.c program listing.
TASK 1
Configure the LED as an output
Suspend itself
DO FOREVER
Flash the LED
ENDDO
TASK 2
Configure pin PA0 to generate external interrupt
DO FOREVER
Wait
ENDDO
ISR
Resume Task 1
IF context switching is required THEN
Call portYIELD_FROM_ISR() to perform context switching
ENDIF
As soon as Task 1 runs it calls API function vTaskSuspend(NULL) to suspend itself at which point this task is blocked and cannot run. Task 2 gets the handle of Task 1 and when the button is pressed Task 1 is resumed from inside the ISR.

19.6. Project 49-Deferred interrupt processing

Description: In this project, Project 48 has been modified so that inside the ISR a flag is set to indicate that an interrupt has occurred. Then, the LED flashing task (TASK 1) is resumed outside the ISR and inside Task 2. This way we can use all of the FreeRTOS API functions outside the ISR.
Aim: The aim of this project is to show how the main processing can be moved to outside the ISR, that is, it shows the concept of deferred interrupt processing.
Block diagram: The block diagram of the project is as in Fig. 19.1.
Program listing: Fig. 19.3 shows the program listing (program: deferred.c). In this version of the program Task 1 suspends itself. When an interrupt occurs a flag is set inside the ISR. Task 2 checks the flag and when it is set resumes Task 1, which then starts the LED flashing.
image
image
Figure 19.3 deferred.c program listing.

19.7. Project 50-Using function xTaskNotifyFromISR()

Description: In this project, Project 49 has been modified so that Task 1 waits for notification before starting to flash the LED. Inside the ISR, function xTaskNotifyFromISR() is called to send a notification to Task 1 so that the LED flashing can start.
Aim: The aim of this project is to show how the FreeRTOS API function xTaskNotifyFromISR() can be used in a project.
Block diagram: The block diagram of the project is as in Fig. 19.1.
Program listing: Fig. 19.4 shows the program listing (program: isrnotify.c). In this version of the program Task 1 waits for notification as soon as it runs. When an interrupt occurs a notification is sent to Task 1 from inside the ISR so that flashing can start. Notice that the macro portYIELD_FROM_ISR() is called to perform a context switching if the priority of the notified task is higher than the running task.
image
image
Figure 19.4 isrnotify.c program listing.
As soon as Task 1 runs, it waits for notification by calling the following API function where the parameter portMAX_DELAY is used so that the task is blocked until a notification is received:
xTaskNotifyWait(0, ULONG_MAX, &ulNotifiedValue, portMAX_DELAY);
If the notification value is 0x02 then Task 1 starts flashing the LED every second.
The ISR notifies Task 1 by the following function call:
xTaskNotifyFromISR(xHandle, 0x02, eSetValueWithOverwrite,&xHigherPriorityTaskWoken);
where xHandle is the handle of Task 1, 0x02 is the notification value which is overwritten at the notified tasks notification value.
External interrupt at pin PA0 is configured and the interrupt priority is set to Level 1.
The handle of Task 1 is obtained by calling function xTaskGetHandle as follows:
const char *pcNameToLookup = "LED Controller";
xHandle = xTaskGetHandle(pcNameToLookup);

19.8. Event group related functions from ISR

Several event group related API functions are available that can be called from ISRs. Commonly used functions are described in the following sections. Interested readers can get further information on these functions from the references given in the Overview section of this chapter.

19.8.1. xEventGroupSetBitsFromISR()

This is the interrupt safe version of the API function xEventGroupSetBits(), where setting a bit unblocks a task that is waiting for that bit to be set. This task has the following format:
  • xEventGroupSetBitsFromISR(EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet,BaseType_t *pxHigherPriorityTaskWoken);
where, xEventGroup is the handle of the event group in which the bits are to be set (the event group must be created before), uxBitsToSet is the bitwise value that indicates the bit (or bits) to be set in the event group, and pxHigherPriorityTaskWoken is the parameter as defined in the other interrupt safe API functions.
The function returns pdTRUE if the message to set the required event flags was sent to the RTOS deamon task.

19.8.2. xEventGroupClearBitsFrmISR()

This is the interrupt safe version of the API function xEventGroupClearBits(), where the specified event flag bits are cleared. The format of the function is:
  • xEventGroupClearBitsFromISR(EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear);
where, xEventGroup is the handle of the event group, and uxBitsToClear is the bitwise value that indicates which bits to be cleared.
The function returns pdTRUE if the message to set the required event flags was sent to the RTOS deamon task.
An example project is given in the next section to illustrate how the function xEventGroupSetBitsFromISR() can be used in a practical application.

19.9. Project 51-Using function xEventGroupSetBitsFromISR()

Description: In this project, Project 49 has been modified so that Task 1 waits for an event flag to be set before starting to flash the LED. Inside the ISR, the API function xEventGroupSetBitsFromISR() is called to set the event flag that Task 1 is waiting for so that the LED flashing can start.
Aim: The aim of this project is to show how the FreeRTOS API function xEventGroupSetBitsFromISR() can be used in a project.
Block diagram: The block diagram of the project is as in Fig. 19.1.
Program listing: Fig. 19.5 shows the program listing (program: isrflag.c). In this version of the program Task 1 waits for event flag 2 to be set as soon as it runs. When an interrupt occurs, event flag 2 is set from inside the ISR so that flashing can start. Notice that the macro portYIELD_FROM_ISR() is called to perform a context switching if the priority of the notified task is higher than the running task.
image
image
image
Figure 19.5 isrflag.c program listing.
An event flag group with the handle xEventGroup is created inside the main part of the program. When Task 1 runs it waits for event flag 2 to be set by calling the following API function:
uxBits = xEventGroupWaitBits(xEventGroup, BIT_2, pdTRUE, pdFALSE, portMAX_DELAY);
This function blocks the task until the event flag is set. When the button is pressed an external interrupt occurs where the program jumps to the ISR named TurnON. Here, event flag 2 is set by calling the following API function:
xResult = xEventGroupSetBitsFromISR(xEventGroup, BIT_2, &xHigherPriorityTaskWoken);
Notice that by setting the event flag Task 1 will be unblocked and it will start to flash the LED every second. The program checks to ensure that event flag 2 has actually been set before starting to flash the LED:
if((uxBits & BIT_2) != 0)                        // Is event flag 2 set?
{
                        while (1)
                        {
                        LED = 1;                        // LED ON
                        vTaskDelay(pdMS_TO_TICKS(1000));                        // Wait 1 second
                        LED = 0;                        // LED OFF
                        vTaskDelay(pdMS_TO_TICKS(1000));                        // wait 1 second
                        }
                        }
If a context switching is requested then the macro portYIELD_FROM_ISR() is called before returning from the ISR.
The following parameters must be set and configured in file freeRTOSConfig.h before compiling the program:
#define configUSE_TIMERS                        1
#define configTIMER_TASK_PRIORITY                        10
#define configTIMER_QUEUE_LENGTH                        10
#define configTIMER_TASK_STACK_DEPTH                        configMINIMAL_STACK_SIZE
#define INCLUDE_xEventGroupSetBitsFromISR                        1
#define INCLUDE_xTimerPendFunctionCall                        1

19.10. Timer related functions from ISR

Several timer related API functions are available that can be called from inside ISRs. Commonly used functions are described in the following sections. Interested readers can get further information on these functions from the references given in the Overview section of this chapter.

19.10.1. xTimerStartFromISR()

This is the interrupt safe version of the API function xTimerStart(). The format of the function is:
  • xTimerStartFromISR(TimerHandle_t xTimer, BaseType_t *pxHigherPriorityTaskWoken);
where, parameter xTimer is the timer handle, and *pxHigherPriorityTaskWoken is as in the other interrupt safe functions. Function returns pdPASS of the command was successfully sent to the timer command queue.

19.10.2. xTimerStopFromISR()

This is the interrupt safe version of the API function xTimerStop(). This function has the same parameters and return values as the xTimerStartFromISR().

19.10.3. xTimerResetFromISR()

This is the interrupt safe version of the API function xTimerReset(). This function has the same parameters and return values as the xTimerStartFromISR().

19.10.4. xTimerChangePeriodFromISR()

This is the interrupt safe version of the API function xTimerChangePeriod(). The format of this function is:
  • xTimerChangePeriodFromISR(TimerHandle_t xTimer, TickType_t xNewPeriod, BaseType_t* pxHigherPriorityTaskWoken);
where, xNewPeriod is the new period of the timer and the other two parameters and the return values are same as function xTimerStartFromISR().
An example project is given in the next section to illustrate how the function xTimerStartFromISR() and xTimerChangePeriodFromISR() can be used in a practical application.

19.11. Project 52-Using functions xTimerStartFromISR() and xTimerChangePeriodFromISR()

Description: In this project two push-button switches and an LED are used. One of the buttons is connected to port pin PA0 and the other one is the on-board button at port pin PA10. The LED at port pin PE12 is used. Pressing PA0 generates an external interrupt which starts a timer from the ISR where the LED is flashed continuously every second at the of the timer period. Pressing button PA10 generates another external interrupt where the period of the flashing changes to every 250 ms.
Aim: The aim of this project is to show how the FreeRTOS API functions xTimerStartFromISR() and xTimerChangePeriodFromISR() can be used in a practical application.
Block diagram: The block diagram of the project is as in Fig. 18.14 where button at PA0 generates an interrupt on the rising edge when pressed, and button at PA10 generates an interrupt on the falling edge when pressed.
Program listing: Fig. 19.6 shows the program listing (program: isrtimer.c). The program consists of one task only in addition to the Idle task. Task 1 controls the buttons at port pins PA0 and PA10. The operation of the program is illustrated by the following PDLs:
image
image
image
Figure 19.6 isrtimer.c program listing.
TASK 1
Configure rising edge external interrupts at port pin PA0
Configure falling edge external interrupts at port pin PA10
Rising Edge ISR (StartTimer)
Start timer with 1 second period
Falling Edge ISR (ChangePeriod)
Change timer period to 250 ms
MAIN
Create a timer with period 1 second and running periodically
Set the callback function to FlashLED
A timer is created in the main program:
xTimer = xTimerCreate("Timer", (pdMS_TO_TICKS(1000)), pdTRUE, 0, vFlashLED);
Task 1 configures both external interrupts, where function SetupPA0() configures interrupts at port pin PA0, and function SetupPA10() configures interrupts at port pin PA10. Pressing button at PA0 starts the timer so that the callback function is called at the end of every second, thus causing the LED to flash every second:
xTimerStartFromISR(xTimer,&xHigherPriorityTaskWoken);
Pressing button at PA10 changes the timer period to 250 ms:
xTimerChangePeriodFromISR(xTimer,(pdMS_TO_TICKS(250)), &xHigherPriorityTaskWoken);
The following parameters must be set and configured in file freeRTOSConfig.h before compiling the program:
#define configUSE_TIMERS                        1
#define configTIMER_TASK_PRIORITY                        10
#define configTIMER_QUEUE_LENGTH                        10
#define configTIMER_TASK_STACK_DEPTH                        configMINIMAL_STACK_SIZE
#define INCLUDE_xEventGroupSetBitsFromISR                        1
#define INCLUDE_xTimerPendFunctionCall                        1

19.12. Semaphore related functions from ISR

A few semaphore related API functions are available that can be called from inside ISRs. Commonly used functions are described in the following sections. Interested readers can get further information on these functions from the references given in the Overview section of this chapter.

19.12.1. xSemaphoreGiveFromISR()

This is the interrupt safe version of the API function xSemaphoreGive(). This function has two parameters: the semaphore handle, and parameter *pxHigherPriorityTaskWoken which has already been described in earlier interrupt safe functions.

19.12.2. xSemaphoreTakeFromISR()

This is the interrupt safe version of the API function xSemaphoreTake(). This function has two parameters: the semaphore handle, and parameter *pxHigherPriorityTaskWoken which has already been described in earlier interrupt safe functions.
An example project is given in the next section to illustrate how the function xSemaphoreGiveFromISR() and xSemaphoreTakeFromISR() can be used in a practical application.

19.13. Project 53-Using functions xSemaphoreTakeFromISR() and xSemaphoreGive()

Description: This project shows how a task and the ISR can be synchronized. There is only one task in the program. A push-button switch is connected to port pin PA0 as in the previous projects and the on-board LED at port pin PE12 is used. The task and ISR are synchronized such that each time the button is pressed an interrupt is created to take the semaphore and then the state of the LED is toggled.
Aim: The aim of this project is to show how the FreeRTOS API functions xSemaphoreTakeFromISR() and xSemaphoreGive() can be used in a practical application.
Program listing: Fig. 19.7 shows the program listing (program: isrsemaphore.c). The program consists of one task only in addition to the Idle task. When the program runs Task 1 configures the LED as output and the button as input. Then, external interrupt is configured on the rising edge of the button. Task 1 then gives the semaphore and the LED is ON. Pressing the button creates an interrupt where the semaphore is taken and therefore the LED is toggled.
image
image
Figure 19.7 isrsemaphore.c program listing.
The binary semaphore is created using the following function call in the main program:xSemaphore = xSemaphoreCreateBinary();
The semaphore is taken using the following function call:xSemaphoreTakeFromISR(xSemaphore, &xHigherPriorityTaskWoken);
The semaphore is given and the LED is toggled using the following function call:
if(xSemaphoreGive(xSemaphore) == pdTRUE)
                        {
                        LED = ∼LED;
                        }

19.14. Queue related functions from ISR

There are several queue related API functions are available that can be called from ISRs. Commonly used functions are described in the following sections. Interested readers can get further information on these functions from the references given in the Overview section of this chapter.

19.14.1. xQueueReceiveFromISR()

This is the interrupt safe version of the API function xQueueReceive(). This function has three parameters: the semaphore handle, a pointer in memory into which the received data will be written, and parameter *pxHigherPriorityTaskWoken which has already been described in earlier interrupt safe functions.

19.14.2. xQueueSendFromISR()

This is the interrupt safe version of the API function xQueueSend(). This function has three parameters as in function xQueueReceiveFromISR(). Notice that there are other interrupt safe queue send operations such as: xQueueSendToBackFromISR (same as xQueueSendFromISR)and xQueueSendToFrontFromISR.

19.15. Project 54-Using functions xQueueSendFromISR() and xQueueReceive()

Description: This project shows how a queue can be setup to send data to a task from inside the ISR. There is only one task in the program. A push-button switch is connected to port pin PA0 as in the previous projects and the on-board LED at port pin PE12 is used. When the button is pressed the external interrupt occurs which sends the flashing rate (250ms) to the task so that the task starts to flash at this rate.
Aim: The aim of this project is to show how the FreeRTOS API functions xQueueSendFromISR() and xQueueReceive() can be used in a practical application.
Program listing: Fig. 19.8 shows the program listing (program: isrqueue.c). The program consists of one task only in addition to the Idle task. When the program runs Task 1 configures the LED as output and the button as input. Then, external interrupt is configured on the rising edge of the button. Task 1 then waits to receive the LED flashing rate from the queue. When the button is pressed the flashing rate is sent to Task 1 which starts the flashing.
image
image
Figure 19.8 isrqueue.c program listing.
The queue is created as follows:#define QUEUE_LENGTH 1#define QUEUE_ITEM_SIZE 2xQueue = xQueueCreate(QUEUE_LENGTH, QUEUE_ITEM_SIZE);
The flashing rate is sent to Task 1 using the following function:xQueueSendFromISR(xQueue, &delay, &xHigherPriorityTaskWoken);
The flashing rate is received by Task 1 and the LED is flashed using the following statements:
if(xQueueReceive(xQueue, &dly, portMAX_DELAY) == pdPASS)
{
                        while (1)
                        {
                        {
                        LED = 1;
                        vTaskDelay(pdMS_TO_TICKS(dly));
                        LED = 0;
                        vTaskDelay(pdMS_TO_TICKS(dly));
                        }
                        }
}

19.16. Summary

In this chapter we have learned how to call the FreeRTOS API functions from inside ISRs. It is important to take care that only the functions ending with FromISR must be used inside the ISRs.
In the next chapters we shall be developing more complex multitasking projects using the FreeRTOS API functions.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.195.110