Tabla de Contenidos

FreeRTOS

Multitasking in Small Embedded Systems

FreeRTOS

image

The simplest case of task priority assignments

Why use a real-time kernel

Benefits of using real-time kernel

Standard FreeRTOS features

1. Task management

1.1 Task scope

1.2 Task functions

   void ATaskFunction (void *pvParameters);

ATaskFunction

   void ATaskFunction( void *pvParameters ) { 
      /* Variables can be declared just as per a normal function.  Each instance 
      of a task created using this function will have its own copy of the 
      iVariableExample variable.  This would not be true if the variable was 
      declared static - in which case only one copy of the variable would exist 
      and this copy would be shared by each created instance of the task. */
 
      int iVariableExample = 0; 
 
      /* A task will normally be implemented as in infinite loop. */ 
 
      for( ;; ) { 
         /* The code to implement the task functionality will go here. */
      } 
 
      /* Should the task implementation ever break out of the above loop 
      then the task must be deleted before reaching the end of this function. 
      The NULL parameter passed to the vTaskDelete() function indicates that 
      the task to be deleted is the calling (this) task. */ 
 
      vTaskDelete( NULL ); 
   } 

1.3 Top level task states

image

1.4 Creating Tasks

    portBASE_TYPE xTaskCreate ( 
        pdTASK_CODE             pvTaskCode, 
        const signed portCHAR   *const pcName,  
        unsigned portSHORT      usStackDepth,  
        void                    *pvParameters,  
        unsigned portBASE_TYPE  uxPriority,  
        xTaskHandle             *pxCreatedTask  
    ); 

All parameters

Example 1 - Creating tasks

image

Example 2 - Using the task parameter

static const char *pcTextForTask1 = "Task 1 is running.\n";
xTaskCreate( vTaskFunction, "Task 1", 240,
   (void*)pcTextForTask1, 1, NULL );
vTaskFunction(void * pvParameters)
   char *pcTaskName;
   pcTaskName = (char *) pvParameters;

1.5 Task priorities

image

Example 3 - Experimenting with priorities

xTaskCreate( vTaskFunction, "Task 2", 240, 
   (void*)pcTextForTask2, 1, NULL );
 To
xTaskCreate( vTaskFunction, "Task 2", 240, 
   (void*)pcTextForTask2, 2, NULL );

image

‘Continuous processing’ task

1.6 Expanding the ‘Not Running’ state

Full task state machine

image

Blocked state

Suspended state

Ready state

Example 4 - Creating a delay

vTaskDelay() API function

void vTaskDelay(portTickType xTicksToDelay);
for( ul = 0; ul < mainDELAY_LOOP_COUNT; ul++ ) { }
vTaskDelay(250 / portTICK_RATE_MS);
/* a period of 250ms is being specified. */

image

image image

vTaskDelayUntil() API Function

void vTaskDelayUntil(
    portTickType *pxPreviousWakeTime,
    portTickType xTimeIncrement);

Example 5 - Converting the example tasks to use vTaskDelayUntil()

vTaskDelay(250 / portTICK_RATE_MS);
/* a period of 250ms is being specified. */
vTaskDelayUntil(&xLastWakeTime,(vTaskDelay(250 / portTICK_RATE_MS));
/* xLastWakeTime init with current tickcount */
xLastWakeTime = xTaskGetTickCount();
/* Updated within vTaskDelayUntil() automatically */

Example 6 - Blocking and non-blocking tasks

void vContinuousProcessingTask(void * pvParameters) {
   char *pcTaskName;
   pcTaskName = (char *) pvParameters;
   for (;;) { vPrintString(pcTaskName);
      for( ul = 0; ul < 0xfff; ul++ ) { } }}
void vPeriodicTask(void * pvParameters) {
   portTickType xLastWakeTime;
   xLastWakeTime = xTaskGetTickCount();
   for (;;){ vPrintString("Periodic task is running\n");
      vTaskDelayUntil(&xLastWakeTime,(10/portTICK_RATE_MS)); }}

image

1.7 Idle task

image

Idle Task Hook Functions

void vApplicationIdleHook(void);

Example 7 Defining an idle task hook function

unsigned long ulIdleCycleCount = 0UL;
/* must be called this name, take no parameters and return void. */
void vApplicationIdleHook (void) {
    ulIdleCycleCount++;
}
vPrintString(pcTaskName)

*

vPrintStringAndNumber(pcTaskName, ulIdleCycleCount);

Idle task and Idle Task Hook

1.8 Scheduling

vTaskPrioritySet(xTaskHandle pxTask, 
   unsigned portBASE_TYPE uxNewPriority);
unsigned portBASE_TYPE uxTaskPriorityGet
   (xTaskHandle pxTask);

Example 8 - Changing task priorities

xTaskHandle xTask2Handle;
xTaskCreate(vTask1, "Task 1", 240, NULL, 2, NULL);
xTaskCreate(vTask2, "Task 2", 240, NULL, 1, &xTask2Handle);
unsigned portBASE_TYPE uxPriority;
uxPriority = uxTaskPriorityGet(NULL);
vTaskPrioritySet(xTaskHandl1, (uxPriority+1));
unsigned portBASE_TYPE uxPriority;
uxPriority = uxTaskPriorityGet(NULL);
vTaskPrioritySet(NULL, (uxPriority-2));

image

1.9 Deleting tasks

void vTaskDelete(xTaskHandle pxTaskToDelete);

Example 9 - Deleting tasks

  1. Task 1 is created by main() with priority 1. When it runs, it creates Task 2 at priority 2. Task 2 as the highest priority task starts to execute immediately.
  2. Task 2 does nothing but delete itself by passing NULL or its own task handle.
  3. When Task 2 has been deleted, Task 1 is again the highest priority task, so continues executing – at which point it calls vTaskDelay() to block for a short period.
  4. The idle task executes while Task 1 is in the blocked state and frees the memory that was allocated to the now deleted Task 2.
  5. When Task 1 leaves the blocked state it again becomes the highest priority Ready state task and preempts the Idle task. Then, start from Step1 again.

image

Memory Management

Summary - 1. Prioritized pre-emptive scheduling

Summary - 2. Selecting Task Priorities

Summary - 3. Co-operative scheduling

2. Queue management

2.1 Queue Management Scope

2.2 Queue Characteristics – data storage

image

image

Blocking on Queue Reads

Blocking on Queue Writes

2.3 Using a Queue

xQueueHandle xQueueCreate(
    unsigned portBASE_TYPE uxQueueLength,
    unsigned portBASE_TYPE uxItemSize);

xQueueSendToBack() and xQueueSendToFront() API Functions

portBASE_TYPE    xQueueSendToBack (
    xQueueHandle xQueue,
    const void   *pvItemToQueue,
    portTickType xTicksToWait);
portBASE_TYPE    xQueueSendToFront (
    xQueueHandle xQueue,
    const void   *pvItemToQueue,
    portTickType xTicksToWait);

xQueueReceive() and xQueuePeek() API Functions

portBASE_TYPE   xQueueReceive (
   xQueueHandle xQueue,
   const void   *pvBuffer,
   portTickType xTicksToWait);
portBASE_TYPE   xQueuePeek(
   xQueueHandle xQueue,
   const void   *pvBuffer,
   portTickType xTicksToWait);

uxQueueMessageWaiting() API Function

unsigned portBASE_TYPE  uxQueueMEssagesWaiting (
        xQueueHandle  xQueue);

Example 10 - Blocking receiving from queue

xStatus = xQueueSendToBack(xQueue,pvParameters,0);
if (xStatus != pdPASS) {
    vPrintString("Could not send to the queue.\n"); 
}
taskYIELD();
xStatus = xQueueReceive(xQueue,&xReceivedValue,100/portTICK_RATE_MS);
if (xStatus == pdPASS) {
    /* print the data received */
}

image

Using Queues to transfer compound types

typedef struct {
  int iValue; // a data value
  int iMeaning; // a code indicating data source
} xData;

image

Example 11 - Queues with registers and blocked time

xStatus = xQueueSendToBack(xQueue,pvParameters,100/portTICK_RATE_MS);
if (xStatus != pdPASS) {
        vPrintString("Could not send to the queue.\n");
}
taskYIELD();
xStatus = xQueueReceive(xQueue,&xReceivedStructure,0);
if (xStatus == pdPASS) {
    /* print the data received */
}

image

2.4 Working with large data

  1. The owner of the RAM being pointed to is clearly defined.
    • When multiple tasks share memory via a pointer, they do not modify its contents simultaneously, or take any other action that cause the memory contents invalid or inconsistent.
      • Ideally, only the sending task is permitted to access the memory until a pointer to the memory has been queued, and only the receiving task is permitted to access the memory after the pointer has been
  2. The RAM being pointed to remains valid.
    • If the memory being pointed to was allocated dynamically, exactly one task be responsible for freeing the memory.
    • No task should attempt to access the memory after it has been freed.
    • A pointer should never be used to access data that has been allocated on a task stack. The data will not be valid after the stack frame has changed.

3. Resources management

3.1 Binary semaphores

* Binary semaphores are the simplest effective way to synchronize tasks, an other even more simple, but not as effective, consists in polling an input or a resource. A binary semaphore can be seen as a queue which contains only one element.

void vSemaphoreCreateBinary( xSemaphoreHandle xSemaphore );
portBASE_TYPE xSemaphoreTake( xSemaphoreHandle xSemaphore, 
   portTickType xTicksToWait );
portBASE_TYPE xSemaphoreGive( xSemaphoreHandle xSemaphore );

image image

3.2 Mutexes

image image

3.3 Counting semaphore routines

xSemaphoreHandle xSemaphoreCreateCounting( 
   unsigned portBASE_TYPE uxMaxCount,
   unsigned portBASE_TYPE uxInitialCount );

4. Interrupts

4.1 Handling interrupts

image

4.2 Manage interrupts using a binary semaphore

 {{freertos_int_sembin.png|image}}
 *
    * An ISR gives a semaphore and unblock a Handler task that is able to handler the ISR, making the ISR execution much shorter.

Example 12 - Semaphores and ISRs

image

4.3 Critical sections

/* Ensure access to the PORTA register cannot be interrupted by
   placing it within a critical section. Enter critical section. */
 
taskENTER_CRITICAL();
 
/* A switch to another task cannot occur between the call to
   taskENTER_CRITICAL() and the call to taskEXIT_CRITICAL(). 
   Interrupts may still execute on FreeRTOS ports that allow 
   interrupt nesting, but only interrupts whose priority is above 
   the value assigned to the configMAX_SYSCALL_INTERRUPT_PRIORITY 
   constant and those interrupts are not permitted to call 
   FreeRTOS API functions. */
 
PORTA |= 0x01;
 
/* We have finished accessing PORTA so can safely leave the critical
section. */
 
taskEXIT_CRITICAL();
/* Write the string to stdout, suspending the scheduler as a method
of mutual exclusion. */
vTaskSuspendAll();
{
    printf( "%s", pcString );
    fflush( stdout );
}
xTaskResumeAll();

5. Memory management

5.1 Prototypes

void *pvPortMalloc( size_t xWantedSize);
void pvPortFree( void *pv);

5.2 Memory allocated once for all

image

5.3 Constant sized and numbered memory

image

5.4 Free memory allocation and deallocation

void *pvPortMalloc( size_t xWantedSize ) 
{ 
void *pvReturn; 
    vTaskSuspendAll(); 
    { 
        pvReturn = malloc( xWantedSize ); 
    } 
    xTaskResumeAll();  
    return pvReturn; 
}
void vPortFree( void *pv ) 
{ 
    if( pv != NULL ) 
    { 
        vTaskSuspendAll(); 
        { 
            free( pv ); 
        } 
        xTaskResumeAll(); 
    } 
}