Tag: STM32F4

STM32 Timers – Output Compare

Keywords: Embedded systems, ARM, STM32F4, Timers, Counter Code Link: Source Code Github Keil- Bare-Metal Code Link: Source Code Github Keil- HAL Libraries In the introductory tutorial, we discussed main building blocks of STM32 Timers. The tutorial can be accessed via the following link. Introduction to […]

STM32F Timers – Simple Up/Down Counter Demo – Blinky LED

Keywords: Embedded systems, ARM, STM32F4, Timers, Counter, LED Code Link: Source Code Github Keil- Bare-Metal Code Link: Source Code Github Keil- HAL Libraries In this tutorial we will go one step further into programming STM32 timers to blink an LED each time the timer count […]

Exchange data between a Task and an ISR via Queue

Keywords: Embedded systems, ARM, FreeRTOS, STM32F4, Tasks, Queues, ISR,

Code Link: Tutorial Source Code Github – Keil

In the previous tutorial we demonstrated how to establish inter-task communication via queue. The link to the tutorial is given bellow.

This tutorial is not much different from the previous one except in this tutorial we will demostrate how to create link between a Hardware interrupt ISR and a freeRTOS Task.

When inter Task-ISR comm is needed:

Most of the embedded systems uses interrupts to handle various on chip and off-chip events. As an example, interrupt generated by a serial buffer indicating data is ready to be received and processed. The data processing may be time consuming and if done inside ISR, it MAY starve normal application flow.

In embedded systems it is highly recommended to keep the ISR as short as possible to avoid blocking/starving of normal application flow especially in time critical systems. The problem becomes more sever if interrupts are arriving at high rate like in case of Serial communication at 115200kbps. The case bacomes even more severe if Interrupt Nesting and Interrupt Re-entrant are allowed.

In order to tackle this problem, queues and binary semaphores (discussed in separate tutorial) best fit. A Task/process responsible for data processing is sent to block state via an empty queue and when interrupt occurs, the Task is unblocked by simply sending a signal via queue and exiting ISR immediately. This achieve two thing, i.e. the ISR remains short and extensive processing is done outside ISR.

Tutorial Scenario:

In this tutorial a freeRTOS task is created that waits for the data to be available on queue. The task remains in block state until an external event (button press in this case) fills the queue. Once the ISR places data on queue, the task is automatically unblocked by freeRTOS kernel. The Task runs and print a message to the debugger serial console.

Button-Press –> ISR –> Queue –> Task

–> Inter Task-Interrupt Communication

Following are the tutorial steps.

Steps:

1. First of all let’s define what messages will be passed to queue. For the purpose of simplicity we will define a global string literal.

/*
  string literals
*/
const char *t1_Msg = "Message received From Button ISR...";

2. Next we will create freeRTOS Queue of length `2` and accepting item size of Pointer to char (char *).

/*
     freeRTOS Queue
*/
qHandle = xQueueCreate(2, sizeof(char *));

qHandle is globally defined variable of type QueueHandle_t.

/*
	freeRTOS Queue Handler
*/
QueueHandle_t qHandle;

3. If queue is created successfully (memory is allocated), a freeRTOS Tasks i.e. “RxTask” is created and Scheduler is started. Otherwise a failed message is sent to serial window and processor is trapped via while(1).

Note: If you don’t know how to create freeRTOS Tasks, refer to the following Tutorial.

if (qHandle != NULL) {
    
  xTaskCreate (vRxTask, "RxTask", 200, NULL, 2, NULL);    
  vTaskStartScheduler();
    
}else {
    
  printf ("Failed to create Queue! :-(\n");
  while (1);
    
}

4. “Inside “RxTask” Task Function”: The following is the definition of “RxTask” Task Function.

void vRxTask(void * pvParams) {

  volatile unsigned int i = 0;
  char * msgPtr;
  
  int rxStatus = 0;
  
  for (;;) {
    
    rxStatus = xQueueReceive(qHandle, &msgPtr, 500);

    if (0 == rxStatus) {
      printf ("Awaiting Message...\n");
    }else {
      printf ("Rx Msg: %s\n", msgPtr);    
    }
  }
}

What happens is:

rxStatus = xQueueReceive(qHandle, &msgPtr, 500); /* 500 is enough to compensate dummy delay in transmitter tasks.*/

xQueueReceive is called to check if data in qHandle Queue is available. If data is available (pointer to string in this case), the data (pointer) is copied into msgPtr pointer variable otherwise the Receiver Task “vRxTask” is sent to Block State waiting for the data to be available in next 500 Ticks. If data is not sent to queue within next 500 Ticks, the message “Awaiting Message…” is displayed and the Receiver Task (vRxTask) goes back to Block State. As soon as the data (pointer to string) becomes available on qHandle Queue, the Receiver task (Rx1) receives the pointer and displays string pointed by the received pointer and wait for the next message (pointer) reception.

5. The next step is to configure User button GPIO on STM32F4-Discovery as input and its interrupt configuration on button press. We already have a dedicated tutorial for STM32F4 interrupts and demo tutorial. Therefor the code will not be repeated here. The link to the tutorial is given bellow. Kindly go through the link bellow and relate it to the source code link given at the start of this tutorial.

6. The next step is to define an ISR for button interrupt. On STM32F4-Discovery User button is connected to PA.0 which produces interrupt on EXTI0 and subsequently calls ISR defined by void EXTI0_IRQHandler (void) functions definition.

Note: See the STM32F4 Interrupt tutorial for more detail. Link given above.

void EXTI0_IRQHandler (void) {

    int txStatus = 0;
    BaseType_t xHigherPriorityTaskWoken;
    /*
        Clear the pending interrupt
    */
    __setbit (EXTI->PR, 0);

      printf("Button Pressed, Sending message to Queue!\n");
  
    txStatus = xQueueSendToBackFromISR(qHandle, &t1_Msg, &xHigherPriorityTaskWoken);

    if (0 == txStatus) {
      
      printf("Sending failed Task-1!\n");
    
    } else {

      portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
	  
    }
}

What happens is:

The __setbit (EXTI->PR, 0); clears the interrupt so that next interrupt/button press can be detected.

printf("Button Pressed, Sending message to Queue!\n"); prints to the user that the button interrupt received and now ISR is about to write to Queue.

txStatus = xQueueSendToBackFromISR(qHandle, &t1_Msg, &xHigherPriorityTaskWoken); statement (API explained in the introductory tutorial) fills the queue with the pointer of string created in step-1 and send it. If queue operation is successful and it caused a higher priority Task to unblock i.e. A low priority Task was running and a Task with higher priority was waiting (blocked) for the qHandle data to be available and mean while the interrupt occurs than the xHigherPriorityTaskWoken flag will be set to indicate to the user to Yield from ISR immediately.

If queue operation is successful, an immediate return from ISR is required DIRECTLY to the high priority Task being unblocked by queue. In freeRTOS in order to switch directly to the that higher priority task being unblocked, portEND_SWITCHING_ISR and portYIELD_FROM_ISR are used and depends upon the freeRTOS Port used. In case of STM32F4 (ARM Cortex-M) the first one is used. portEND_SWITCHING_ISR will cause the kernel to directly jump to the high priority task instead of returning from ISR to the low priority task and then waiting for the context switch upon which the high priority will be selected. This minimize the delay between unblocking a Task from an ISR and subsequently achieving perfect synchronization.

Now as soon as the button is pressed, EXTI0_IRQHandler will be called. The EXTI0_IRQHandler will fill the queue with string pointer and return immediately. Once the data is available in queue, the “vRxTask” will be unblocked to receive the string pointer from queue and print the string pointer by the sent pointer thus achieving inter Task-ISR Communication.

7. Finally Compile source code, connect STM32F4-Discovery board to PC and enter debug mode. Open printf serial window via View -> Serial Windows -> Debug (printf) Viewer. Run the Program, Press the Blue User button, You will see printf messages as shown in bellow Video.

Note: We have routed printf messages to ST-Link debugger via ARM-ITM. There is a dedicated tutorial on how to redirect printf messages to debugger. Link to the tutorial is given bellow.

For complete source code refer to Github link given at the start of this tutorial.

Click the full screen button for more clear view.

References:

[1] – FreeRTOS Official Website

Inter Tasks Communication via Queues

Keywords: Embedded systems, ARM, FreeRTOS, STM32F4, Tasks, Queues Code Link: Tutorial Source Code Github – Keil In the previous tutorial we introduced Queues in general and freeRTOS queues in specific. We introduced freeRTOS APIs for Queues management. The link to the tutorial is given bellow. […]

Introduction to FreeRTOS Queues

Keywords: Embedded systems, ARM, FreeRTOS, STM32F4, Tasks, Queues “A Queue is a Data Structure that holds a finite number of fixed-size data items and follows FIFO (First in First Out) access mechanism”. Most of the low power low end embedded systems follows legacy hardware architecture […]

FreeRTOS – Task Yielding

Keywords: Embedded systems, ARM, FreeRTOS, STM32F4, Tasks, Tasks Yielding

Code Link: Tutorial Source Code Github – Keil

As we know from introductory tutorial that scheduler divides time line into small time slots based on tick timer. In each time slot only one task is selected and allowed to run. When the tick timer ticks again, another ready task is selected to run while the current task is sent to ready state.

Whats the problem with this approach?

As each task is allowed fixed time slot based on Tick timer ticking frequency, what if a task requires more time to complete than the alloted time slot? Similarly if a task completes its functionality before its time slot finishes than what should it do? For example a task dedicated to take some sensors data on regular bases – Figure-1. Lets say data acquisition requires 10% of time slot than what the task suppose to do in remaining 90% time?

In FreeRTOS a task runs indefinitely. It can’t return whether it finishes earlier or past the time slot. But FreeRTOS has a way around for tasks that completed their functionality earlier. The solution is called Task Yielding. In FreeRTOS a task can handover processor cycles (meant for it) any time it wants by calling FreeRTOS Task Yielding API. Using Task Yielding API a task can inform Scheduler to hand over processor cycles to another ready task as it has completed its functionality and can’t wait for tick timer based context switching!

Figure-1: Dummy Task that is required to run after specific Time

The FreeRTOS Task Yielding API is given bellow.

  • void taskYIELD( void )

void taskYIELD(void):

taskYIELD() is used to request a context switch to another task. Yielding occurs when a task volunteers to leave the Running state without being preempted and before its time slice has been fully used.

taskYIELD() must only be called from an executing task and therefore must not be called while the scheduler is in the Initialization state (before the scheduler is started).

When a task calls taskYIELD(), the scheduler will select another Ready state task of equal priority to enter the Running state in its place. If there are no other Ready state tasks of equal priority, then the task that called taskYIELD() will be transitioned into the Running state.

Parameter NameDescription
voidvoid
Return Valuevoid

In all our previous tutorials we put some delay in each task to ensure task consumes enough time to exceed time slot and same functionality is not executed multiple times with the same time slot. But real time Embedded Systems are quite different. They have deadlines to meet. They can’t wast time just to consume complete time slot. In real time systems, if a task completes, it must allow other tasks in queue to take consume processor cycles. The purpose of task yielding is simply to allow other tasks to execute once it completes its own functionality.

Tutorial Scenario:

In this tutorial we will create two FreeRTOS tasks. Each task when runs, it simply prints a message. After printing message it has nothing else to do, so instead of waiting for the context switch, it simply Yield to scheduler by calling FreeRTOS Scheduler API i.e. taskYIELD().

Following are the tutorial steps.

Steps:

1. The first step is to create two FreeRTOS Tasks i.e. “T1” and “T2” are created.

Note: If you don’t know how to create freeRTOS Tasks, refer to the following Tutorial.

const int task_1_Param = 1;
const int task_2_Param = 2;

xTaskCreate (vTask, "T1", 150, (void *)&task_1_Param, 1, NULL);

xTaskCreate (vTask, "T2", 150, (void *)&task_2_Param, 1, NULL);

2. Next Step is to create a task function which will be called by FreeRTOS Tasks when scheduled.

void vTask(void * pvParams) {

  volatile unsigned int i = 0;
  const int * tParam = (const int *)pvParams;
 
  for (;;) {
    
    /*
      Do what every Task is meant to do
    */
    printf("Task-%d Running.\n", *tParam);
    
    /*
      Now that task has completed its function,
      so let's yield so that other tasks are 
      waiting to be scheduled are allowed to 
      run.
    */
    printf("Task-%d Complete - Yielding Task.\n\n\n", *tParam);
    taskYIELD();
    
  }
}

As can be seen from above task function implementation, the task prints the a message to debug console and once it prints the message it has noting more to do. So in order to handover processor to other task it simply calls the task Yield API taskYIELD(). Calling the taskYIELD() immediately calls initiate context switch and allow other task to run before the tick timer based context switch occurs.

3. The last step is to start the scheduler to allow created tasks to run.

/*
    Start the Scheduler
*/
vTaskStartScheduler();

Note: We have routed printf messages to ST-Link debugger via ARM-ITM. There is a dedicated tutorial on how to redirect printf messages to debugger. Link to the tutorial is given bellow.

For complete source code refer to Github link given at the start of this tutorial.

Click the full screen button for more clear view.

References:

[1] – FreeRTOS Official Website

FreeRTOS – Delaying Tasks

Keywords: Embedded systems, ARM, FreeRTOS, STM32F4, Tasks Delay, Tasks Code Link: Tutorial Source Code Github (vTaskDelay)- Keil Code Link: Tutorial Source Code Github (vTaskDelayUntil)- Keil By default any FreeRTOS task should run indefinitely under normal operation. This means a task will always be scheduled to […]

FreeRTOS – Deleting Tasks

Keywords: Embedded systems, ARM, FreeRTOS, STM32F4, Tasks, Task Deletion Code Link: Tutorial Source Code Github – Keil In the previous tutorials we showed how to create tasks, pass parameters to task, and how to change tasks priority on fly. It is highly recommended to go […]

FreeRTOS – Changing Tasks Priorities

Keywords: Embedded systems, ARM, FreeRTOS, STM32F4, Tasks, Task Priorities

Code Link: Tutorial Source Code Github – Keil

In the previous tutorials we showed how to create FreeRTOS Tasks and pass parameters to it. In this tutorial we will demonstrate two things.

  1. Effect of Task Priority on Task Execution
  2. Changing Task priority on fly

Note: The tutorial assumes Priority based Preemptive Scheduling (The default scheduling algorithm of FreeRTOS) according to which Tasks are executed based on Task priorities. Higher the priority, the first it will be executed.

As we mentioned earlier that scheduler always run the highest priority Ready task (in Priority based Preemptive Scheduling algorithm). In this tutorial we will create two FreeRTOS Tasks (Task-1, Task-2) with equal priorities. After 10 Task Switching iterations, we will rise Task Task-1 priority such that:

Priority of Task-1 > Priority of Task-2

Now that the Task-1 has the higher priority so as per scheduler policy it will keep running forever. This is because each time the scheduler tick interrupt occurs (to switch task), it finds Task-1 as the highest priority Ready task in queue.

Task-1 will keep running until another higher priority task gets ready or the priority of Task-1 is made lower/equal to Task-2. We will let Task-1 keep running for 10 task switching iteration, after that we will lower the Task-1 priority to be equal to Task-2 and vise versa. The process is summarized in Figure-1.

Figure-1: Changing Task-1 Priority after each 10 Task Switching Iterations.

As shown in the Figure-1, for the first 10 iterations both Task-1 and Task-2 runs alternatively as both have equal priority. At 10th iteration, the priority of Task-1 is increased. Now for next 10 iterations we kept Task-1 priority higher than Task-2 so only Task-1 will be allowed to run as per scheduling policy. After 10 iterations (at 20 iteration in total), we again lowered the Task-1 priority to be equal to Task-2. Now as both tasks again have equal priority so both will run alternatively and vise versa.

Task Priority APIs:

Two APIs are used to inquire freeRTOS Tasks Priorities.

  1. UBaseType_t uxTaskPriorityGet( TaskHandle_t xTask );
  2. void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority );

uxTaskPriorityGet(…):

This API returns the task priority of handle xTask given as argument. Passing a NULL handle results in the priority of the calling task being returned.

Note: In FreeRTOSConfig.h header file, INCLUDE_vTaskPriorityGet must be defined as 1 for this function to be available.

vTaskPrioritySet(…):

This API sets the task priority of handle xTask given as argument to the new value given in parameter uxNewPriority. Passing a NULL handle results in setting the priority of the calling task.

Note: In FreeRTOSConfig.h header file, INCLUDE_vTaskPrioritySet must be defined as 1 for this function to be available.

Let’s jump to programming.

Steps

1. First of all declare TaskHandle_t handler for Task-1 which will be used in Task Priority APIs.

TaskHandle_t hTask1;

2. Next create two FreeRTOS Tasks of equal priorities. As we will change Task-1 priority on fly so we need to refer to Task-1 later in code. For this purpose we need a task handler. we have already declared a global TaskHandle_t handler in previous step. Here we will only pass its pointer to xTaskCreat(...) API.

  const int task_1_Param = 1;
  const int task_2_Param = 2;

  xTaskCreate (vTask, "T1", 150, (void *)&task_1_Param, 1, &hTask1);

  xTaskCreate (vTask, "T2", 150, (void *)&task_2_Param, 1, NULL);

3. Define a counter variable to be used to count number of times tasks being switched.

int counter = 0;

4. Upon 10th iteration, raise the priority of Task-1 by 1. At this point forward only Task-1 will run as it has the higher priority than Task-2.

if (counter == 10) {
      
    printf("Raising Task-1 Priority....\n"); 
    vTaskPrioritySet(hTask1, uxTaskPriorityGet(hTask1)+1);   
}

5. Upon 20th iteration, lower the priority of Task-1 by 1. At this point forward both Tasks will run alternatively as both Tasks have equal priorities.

if (counter == 20) {
     
    printf("Lowering Task-1 Priority....\n");
    vTaskPrioritySet(hTask1, uxTaskPriorityGet(hTask1)-1);
    counter = 0;
}

The complete Task function code is given bellow.

void vTask(void * pvParams) {

  volatile unsigned int i = 0;
  const int * tParam = (const int *)pvParams;
 
  for (;;) {
    
    printf("Task-%d Running.\n", *tParam);  
    
    counter++;
    if (counter == 10) {
      
      printf("Raising Task-1 Priority....\n"); 
      vTaskPrioritySet(hTask1, uxTaskPriorityGet(hTask1)+1);
    
    }else if (counter == 20) {
      
      printf("Lowering Task-1 Priority....\n");
      vTaskPrioritySet(hTask1, uxTaskPriorityGet(hTask1)-1);
      counter = 0;
    }
    
    /*Dummy Delay - Lazy work */
    for (i =0; i < 500000; ++i);
  }
}

5. Start the Scheduler.

  vTaskStartScheduler();

6. Finally Compile source code, connect STM32F4-Discovery board to PC and enter debug mode. Open printf serial window via View -> Serial Windows -> Debug (printf) Viewer. Run the Program, You will see Task execution messages as shown in bellow Video.

Note: We have routed printf messages to ST-Link debugger via ARM-ITM. There is a dedicated tutorial on how to redirect printf messages to debugger. Link to the tutorial is given bellow.

For complete source code refer to Github link given at the start of this tutorial.

Click the full screen button for more clear view.

References:

[1] – FreeRTOS Official Website

FreeRTOS Simple Tasks – Passing Parameters

Keywords: Embedded systems, ARM, FreeRTOS, STM32F4, Tasks Code Link: Tutorial Source Code Github – Keil In the previous tutorial we explained how to create simple freeRTOS tasks. We created two tasks and used separate task function for each task – also no parameters were passed […]